Yii2 - 按最近的hasMany关系搜索

时间:2017-04-06 09:33:24

标签: yii2 relation

我有名为Task和Test的ActiveRecord模型。任务可能包含多个测试(或零)。

Task模型中的关系:

public function getTests(){
    return $this->hasMany(Test::className(), ['task_num' => 'task_num']);
}

public function getLastTest(){
    return $this->hasOne(Test::className(), ['task_num' => 'task_num'])->addOrderBy(['test_num' => SORT_DESC])->limit(1);
}

正如预期的那样,lastTest关系并不像我想要的那样起作用。我需要这种关系来进行搜索。我需要过滤所有状态等于3且具有测试且最新状态不等于4的任务。

TaskSearch型号:

public function search($params)
{
    $query = Task::find();

    $dataProvider = new ActiveDataProvider([
        'query' => $query,
        'pagination' => [
            'pageSize' => 10,
        ],
    ]);

    // standart search content
    // ...
    // ...


    // TASK_COMPLETE is 3
    // TEST_COMPLETE is 4
    $query->andFilterWhere(['tbl_task.status' => TASK_COMPLETE]);
    $query->joinWith(['lastTest' => function($q){
        $q->where('tbl_test.status <> ' . TEST_COMPLETE);
    }]);    

    return $dataProvider;
}

产生的sql查询是:

SELECT `tbl_task`.*
FROM `tbl_task`
LEFT JOIN `tbl_test` ON `tbl_task`.`task_num` = `tbl_test`.`task_num`
WHERE (`tbl_task`.`status`=3) AND (tbl_test.status <> 4)
ORDER BY `task_num` DESC, `test_num` DESC
LIMIT 10

此查询查找至少一个测试的所有任务,其中状态不等于4,但我只需找到只有最后一次测试状态不等于4的任务或根本没有测试。

如何定义必要的关系并设置搜索方法?

编辑:

通过试验和错误我找到了正确的SQL查询:

SELECT t1.*
FROM tbl_task t1
LEFT JOIN tbl_test t2 ON t1.task_num = t2.task_num
WHERE
   (t1.status = 3) AND 
   (t2.status <> 4) AND
   (t2.test_num = (SELECT MAX(t3.test_num) FROM tbl_test t3 LEFT JOIN tbl_task t4 ON t3.task_num = t4.task_num))

如何从search方法进行此查询?

编辑2:

我错了,这个查询也不起作用。

2 个答案:

答案 0 :(得分:1)

最后通过子查询得到它。

我的解决方案是:

$query->andFilterWhere(['tbl_task.status' => TASK_COMPLETE]);
$query->joinWith(['lastTest' => function($q){
   $q->where('tbl_test.status <> ' . TEST_COMPLETE);
}]);
$query->andWhere('(tbl_test.test_num = (SELECT MAX(t1.test_num) FROM tbl_test t1 LEFT JOIN tbl_task t2 ON t1.task_num = t2.task_num WHERE t2.task_num = tbl_task.task_num GROUP BY t2.task_num))');

不是非常干净的解决方案,但它确实有效。

答案 1 :(得分:0)

试试这个:

public function getLastTest(){
    return $this->getTests()->addOrderBy(['test_num' => SORT_DESC])->limit(1);
}

然后这个:

$query = Task::find();
$query->joinWith('lastTest as lt');
$query->andFilterWhere(['tbl_task.status' => TASK_COMPLETE]);
$query->andFilterWhere('lt.status <> ' . TEST_COMPLETE);