Cakephp 3 NOT IN 查询

8

我认为这是一个常见的模式,但我找不到优雅的CakePHP方法来实现它。思路是从已选择项列表中删除值。以Cake书中的例子为例:

table Students (id int, name varchar)
table Courses (id int, name varchar)
table CoursesMemberships (id int, student_id int, course_id int, days_attended int, grade varchar)

我希望做的是创建一个查询,返回给定学生尚未注册的课程,很可能用于填充选择下拉列表。

如果我没有使用Cake,我会这样做

select * from Courses where id not in 
    (select course_id from CoursesMemberships where student_id = $student_id)

也许使用等效的NOT EXISTS子句或外连接技巧,但你已经有了想法。
我不知道如何在Cake中优雅地完成这个操作。我认为这应该是一个常见的需求,但我研究了一段时间,并尝试了一些查询方法,但都没有成功。
3个回答

18
如果您想使用子查询,只需将查询对象作为条件值传递,例如:
$subquery = $Courses->CoursesMemberships
    ->find()
    ->select(['CoursesMemberships.course_id'])
    ->where(['CoursesMemberships.student_id' => $student_id]);

$query = $Courses
    ->find()
    ->where([
        'Courses.id NOT IN' => $subquery
    ]);

作为替代,也可以使用Query::notMatching()(CakePHP 3.1及以上版本可用),它可以用于选择其关联记录不符合特定条件的记录:
$query = $Courses
    ->find()
    ->notMatching('CoursesMemberships', function (\Cake\ORM\Query $query) use ($student_id) {
        return $query
            ->where(['CoursesMemberships.student_id' => $student_id]);
    });

另请参阅


6
在CakePHP 3中,您可以像这样创建“NOT IN”查询。
$query = $Courses->find()
    ->where(['id NOT IN' => $ids]);

在CakePHP 3中,您可以像这样创建IN查询。
$query = $Courses->find()
    ->where(['id IN' => $ids]);

你可以在CakePHP 3 Cookbook - Creating IN Clauses中了解它。

我使用了一种更复杂的机制(请参见上面的注释),成功地使其工作。但是我正在寻找的是一种一次性完成此查询的方法,而不是查询现有记录,将它们强制转换为数组,然后再使用上述方法。 - Roger Kaplan
请看此例子:http://book.cakephp.org/3.0/zh/orm/query-builder.html#subqueries - user3082321

3

我认为最优雅的答案是使用notMatching()选项:

$data = $this->Courses->find("list")
                      ->notMatching("Students", 
                         function($q) use ($student_id) {
                            return $q->where(["Students.id"=>$student_id]);
                         }
                      );

假设学生有多门课程,而每门课程也有多名学生。
我认为这是最优雅的答案,因为它不依赖于任何SQL,并仅使用Cake的语义来表示我想要实现的逻辑。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接