V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Mitt
V2EX  ›  PHP

Yii2 中在复杂查询的时候有没有更为优雅的写法

  •  1
     
  •   Mitt · 2017-04-15 01:58:15 +08:00 · 3931 次点击
    这是一个创建于 2780 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RT 场景是这样的:

    Mission 表存任务
    {
       id, 
       count 最大订单数
    }
    
    Order 表存订单
    {
       user,  接单用户
       status 订单状态  分别有 created 、 processing 、 finished 、 canceled
    }
    

    一个用户接受一个任务 就新增一份订单 状态为 created


    Q1: 获取任务列表额外返回每个任务的完成数量和可接数量

    任务订单数(条件为 status 必须为 created 、 processing 、 finished) 任务订单完成数(条件为 status 必须为 finished)

    Q2: 在 Q1 的基础上判断可接任务

    这个时候要获取条件 (任务订单数 <= Mission.count) 的任务列表

    Q3: 是否能将 Q1 、 Q2 的语句拆分到 ActiveQuery | Query 来实现复用

    RT

    暂时就想到这三个

    1. 如何在能确保可复用性的情况下实现更为优雅的写法
    2. 尽量在任务和订单过多的情况下产生较少的查询语句

    Ps: 在发这个帖子之前我是有做过一定量相关查询的,但是由于关键字用的不好、英语不好查不到太多国外例子等原因导致我并没有找到任何方案 所以想发个帖子讨论一下 如果能有相关的链接(中英都可以)也可以发上来学习一下 最好是有相关有质量的开源项目可以做参考

    PPs: 排版比较辣鸡轻喷。一定抽空学 MarkDown

    第 1 条附言  ·  2017-04-15 18:19:12 +08:00
    
    // Mission Model
    class Mission extends ActiveRecord
    {
        public $complete_count;
        public $order_count;
        ...
        public function rules()
        {
            return [[['id', 'count'], 'safe]];
        }
    
       public function getOrders()
       {
           return $this->hasMany(Order::className(), ['mission_id' => 'id']);
       }
       ....
    }
    
    // Order Model
    class Order extends ActiveRecord
    {
        const STATUS_CREATED = 0;
        const STATUS_COMPLETE = 1
        const STATUS_PROCESSING = 2;
        const STATUS_FAILED = 3
        const STATUS_CANCELED = 4;
    
        public function rules()
        {
            return [
                [['user', 'mission_id', 'status'], 'safe']
            ];
        }
    
        public function getMission()
        {
            return $this->hasOne(Mission::className(), ['id' => 'mission_id']);
        }
    
        public function getUser()
        {
            return $this->hasOne(User::className(), ['id' => 'user']);
        }
    
        public function find()
        {
            return new OrderQuery(get_called_class());
        }
    }
    
    // User Model
    class User extends ActiveRecord
    {
        public function rules()
        {
            return [['id', 'safe']];
        }
    }
    
    // Order Query
    class OrderQuery extends ActiveQuery
    {
        public function status($status)
        {
            return $this->andWhere(['status' => $status]);
        }
    }
    
    
    
    第 2 条附言  ·  2017-04-15 18:21:13 +08:00
    
    // 截止睡觉前想到我的写法
    public function actionA()
    {
        $missionQuery = Mission::find()->alias('mission');
        $missionQuery->leftJoin(Order::tableName() . ' order', '`order`.mission_id = mission.id');
        // Count Query
        $missionCountQuery = Order::find()->alias('order')->select(new Expression('count(*)'))->where('`order`.mission_id = `mission`.id')->status([Order::STATUS_COMPLETE, Order::STATUS_PROCESSING]);
    
        // where (select...) < mission.count
        $missionQuery->andWhere('(' . $missionCountQuery->createCommand()->sql . ') < mission.count');
    
        $data = $missionQuery->all();
        return $data;
    }
    
    // 发帖前的写法
    public function actionB()
    {
        $query = MissionPlatform::find()->alias('p')->select('*')->orderBy('id desc');
        $missionOrderCountQuery = Order::find()->select(['count' => new Expression('count(*)')])->alias('o');
        $missionCompleteCountQuery = clone $missionOrderCountQuery;
        $query->addSelect([
            'order_count' => $missionOrderCountQuery->where('o.mission_id = p.id'),
            'complete_count' => $missionCompleteCountQuery->where('o.mission_id = p.id and o.status in :status', [':status' => [Order::STATUS_COMPLETE, Order::STATUS_PROCESSING]])
        ]);
        // 通过having过滤
        $query->andHaving('complete_count < p.count');
        
        $data = [];
        foreach($query->each(100) as $mission)
        {
            $tmp = $mission->toArray(['id', 'count']);
            $tmp['order_count] = $mission->order_count;
            $tmp['complete_count'] = $mission->complete_count;
            
            $data[] = $tmp;
        }
    
        return $data;
    }
    
    第 3 条附言  ·  2017-04-15 18:22:00 +08:00
    /**
        我承认脑子一抽扔了优雅俩字到帖子标题上 实际却像是跟别人要成品,
        因为发帖的时候刚熬完夜,所以还没整理代码就睡着了,我也是发完才想起来应该扔份代码 
        但是因为自己写的实际代码要比帖子上举例的更复杂(更无用)一点,所以就没贴上,起来发现被喷了,确实是我的不对
        希望各位大佬不要嫌弃小菜给小菜提点建议
        Ps:
            1. 由于本人是个数据库弱痴,所以连查都无从下手的,如果更好的解决办法是关于数据库方面的 希望能不嫌弃丢我两个相关关键词
            2. 关于表关联关系 上述的模型定义应该能看的懂的吧
            3. 我上面的 $missionCountQuery->createCommand()->sql 方式我认为特别不合适。暂时没深入了解这块就没继续改(因为发帖的时候我还在赶这个东西)
            4. rules 定义希望不要吐槽,我实际写模型不会这么写的,只是瞎写一下确保有那么几个字段就好了
    **/
    
    16 条回复    2017-04-17 10:11:29 +08:00
    young
        1
    young  
       2017-04-15 08:40:13 +08:00
    增加几个计数字段不可以吗?
    qce7
        2
    qce7  
       2017-04-15 09:16:11 +08:00
    两个表关联关系,外键都没有,别人怎么给你回答
    bombless
        3
    bombless  
       2017-04-15 09:19:19 +08:00 via Android
    看标题说有没有更优雅的写法我还以为已经写了一版让人来改呢……结果是还没写想要别人写现成的是吧……
    reus
        4
    reus  
       2017-04-15 14:15:46 +08:00
    你根本就是不会!
    Mitt
        5
    Mitt  
    OP
       2017-04-15 18:24:56 +08:00
    @young 计数字段是指什么 插入或者修改订单的时候 向任务的计数字段+1 or -1 ?
    @qce7 @bombless @reus 我又重新补充了一份又臭又长的代码上去。给各位大佬道歉了
    Mitt
        6
    Mitt  
    OP
       2017-04-15 18:32:13 +08:00
    @reus 虽然我用这框架也有一段时间了,但是确实不敢承认自己会这框架,写的几个项目都是堆代码到控制器里的,我自己看的都恶心。以我的现在的水平直接看框架源码确实有些看不懂,所以我更倾向看成品源码去学习深入框架的各种用法
    miaotaizi
        7
    miaotaizi  
       2017-04-15 20:55:50 +08:00 via iPhone
    @Mitt 看着恶心就想办法让他不恶心,伸手是最没效率的,善用 google
    Mitt
        8
    Mitt  
    OP
       2017-04-15 21:58:11 +08:00
    @miaotaizi 其实我发这帖子之前就已经 Google 了很长时间了(至少 6 个小时) 因为我自己搜的关键词大多是中文 英文极少数 搜出来的大多是新手教程 不是我想要的,所以才想来这里发帖讨论一下对于一种场景大家是怎么做的, 对于伸手 我也认同你的观点 但是我不认为我是纯粹的伸手党,如果你真的看了我的帖子 那你应该知道 我更想要的是相关文档 帖子 链接 关键词 , 特别是关键词 我认为我不是不善用 Google 而是没有正确使用合适的关键词 所以找出来一大堆不相干的东西,“看着恶心就想办法让他不恶心” 这是我发这个帖子是目的之一。可能我说这句话不太合适,但是我需要的是别人的“指引”。 // 以上语句表述没有恶意成分
    young
        9
    young  
       2017-04-15 22:38:17 +08:00
    @Mitt 对的, 至少代码不恶心
    Mitt
        10
    Mitt  
    OP
       2017-04-16 00:15:56 +08:00
    @young 确实很适用这个场景,很感谢你的回复。
    cszchen
        11
    cszchen  
       2017-04-16 08:14:47 +08:00
    你的表有哪些字段都没说清楚,看了你的代码才大概知道你的表还有哪些字段,好累,这样不好,还是应该贴完整点的。
    另外不要想着一个方法搞定所有的事,一个方法就只应该完成一件事,这样就很容易实现了,也才优雅。
    cszchen
        12
    cszchen  
       2017-04-16 08:16:49 +08:00
    比如在 Order Model 里增加一个静态方法,返回特定任务特定状态的数量
    controller 中调用和并做一下加减运算,即能复用,又很简单
    Mitt
        13
    Mitt  
    OP
       2017-04-16 22:35:42 +08:00
    @cszchen 字数限制 我主要想把 sql 语句整合到一起的原因是可能还牵扯到另一个表,这个时候如果自己计算可能查询语句会指数增长(然而肯定是我想的多又不做优化)

    很感谢你的回复
    miaotaizi
        14
    miaotaizi  
       2017-04-17 09:12:56 +08:00
    @miaotaizi
    `关键词大多是中文 英文极少数` 既然知道了, 就去改进呗, 搜索功底也是门技能.
    qce7
        15
    qce7  
       2017-04-17 09:23:10 +08:00
    可以考虑在 mission 中加几个字段,任务订单数,任务订单完成数
    这样所有的问题都简化了
    Mitt
        16
    Mitt  
    OP
       2017-04-17 10:11:29 +08:00
    @miaotaizi 英语是要学的 但是这不是一段时间就能学会的,所以在此之前还是要借助一点他人的搜索技巧的

    @qce7 因为我举的例子只是一个核心问题,实际做的东西要关联的更多一点,所以加字段可能不太适合,但是毫无疑问这个方案会解决大部分问题。

    很感谢你们的回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1212 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:21 · PVG 02:21 · LAX 10:21 · JFK 13:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.