CakePHP 3.x - hasMany通过关联 - 查找

Zbi*_*woń 4 cakephp cakephp-3.0

假设我在CookBook中有完整的设置:http: //book.cakephp.org/3.0/en/orm/associations.html

class StudentsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Courses', [
            'through' => 'CourseMemberships',
        ]);
    }
}

class CoursesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Students', [
            'through' => 'CourseMemberships',
        ]);
    }
}

class CoursesMembershipsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Students');
        $this->belongsTo('Courses');
    }
}

Student BelongsToMany Course
Course BelongsToMany Student

id | student_id | course_id | days_attended | grade
Run Code Online (Sandbox Code Playgroud)

我应该如何构建查询以查找给定学生的课程,他的成绩为=="A"?

$query = $this->Courses->find('all')
    ->contain(['CourseMemberships'])
    ->where(['CourseMemberships.student_id' => $student['id'], 'CourseMemberships.grade' => 'A']);
Run Code Online (Sandbox Code Playgroud)

这不行.我该怎么写呢?

ndm*_*ndm 19

通常你会使用匹配,但是ORM似乎不支持在连接表"关联"上匹配,因为它们在那时不是"真正的"关联(你可能想建议作为增强),它们正在稍后补充道.

matching() 解决方法

什么工作是使用matching()where()外部查询,即

$query = $this->Courses
    ->find('all')

     // contain needs to use `Students` instead (the `CourseMemberships`
     // data can be found in the `_joinData` property of the tag),
     // or dropped alltogether in case you don't actually need that
     // data in your results
    ->contain(['Students'])

     // this will do the magic
    ->matching('Students')

    ->where([
        'CourseMemberships.student_id' => $student['id'],
        'CourseMemberships.grade' => 'A'
    ]);
Run Code Online (Sandbox Code Playgroud)

这将使用别名加入表studentscourses_students连接表CourseMemberships,例如

INNER JOIN
    students Students ON 1 = 1
INNER JOIN
    courses_students CourseMemberships ON (
        Courses.id = (CourseMemberships.course_id)
        AND Students.id = (CourseMemberships.student_id)
    )
Run Code Online (Sandbox Code Playgroud)

因此可以应用条件.这感觉就像一个不太好的解决方法.

使用额外的关联(可能是更好的方法)

另一种选择是添加另一个显式关联(如提到的那种@AtaboyJosef),即hasMany连接表的关联(这将在稍后的时间点自动完成,但正如已经提到的那样,为时已晚matching()).

请注意,这将需要命名连接表course_memberships!

class CoursesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Students', [
            'joinTable' => 'course_memberships',
            'through' => 'CourseMemberships',
        ]);

        $this->hasMany('CourseMemberships', [
            'foreignKey' => 'course_id'
        ]);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以在CourseMemberships关联上使用匹配

$query = $this->Courses
    ->find('all')
    // with this solution you can also use contain for `CourseMemberships`
    ->contain(['CourseMemberships'])
    ->matching('CourseMemberships', function(\Cake\ORM\Query $query) use ($student) {
        return $query->where([
            'CourseMemberships.student_id' => $student['id'],
            'CourseMemberships.grade' => 'A'
        ]);
    });
Run Code Online (Sandbox Code Playgroud)

应该创建一个类似的查询

INNER JOIN course_memberships CourseMemberships ON (
    CourseMemberships.student_id = 1 
    AND CourseMemberships.grade = 'A' 
    AND Course.id = (CourseMemberships.course_id)
)
Run Code Online (Sandbox Code Playgroud)

这可能会更有效,因为它需要更少的选择.