【问题标题】:CakePHP 3.x - hasMany through association - findCakePHP 3.x - hasMany 通过关联 - 查找
【发布时间】:2015-07-27 01:54:20
【问题描述】:

假设我的设置与 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

我应该如何构造查询来查找给定学生的课程,他的成绩 ==“A”?

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

这行不通。应该怎么写?

【问题讨论】:

  • 此代码触发错误:“Courses is not associated with CourseMembers”

标签: cakephp cakephp-3.0


【解决方案1】:

通常您会使用matching,但 ORM 似乎不支持在连接表“关联”上进行匹配,因为此时它们不是“真正的”关联(您可能想要suggest that as an enhancement),稍后会添加它们。

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'
    ]);

这将使用CourseMemberships 别名加入students 表以及courses_students 连接表,例如

INNER JOIN
    students Students ON 1 = 1
INNER JOIN
    courses_students CourseMemberships ON (
        Courses.id = (CourseMemberships.course_id)
        AND Students.id = (CourseMemberships.student_id)
    )

因此可以应用条件。感觉这不是一个很好的解决方法。

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

另一种选择是添加另一个显式关联(就像提到的@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'
        ]);
    }
}

这样您就可以在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'
        ]);
    });

应该创建一个类似的查询

INNER JOIN course_memberships CourseMemberships ON (
    CourseMemberships.student_id = 1 
    AND CourseMemberships.grade = 'A' 
    AND Course.id = (CourseMemberships.course_id)
)

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

【讨论】:

  • 广告。 “matching() 解决方法” - 我添加了“matching(...)” 行,但我仍然得到 - “Courses is not associated with CourseMemberships”
  • @ZbigniewLedwoń 那是因为在这种情况下contain() 不应该存在(或使用Tags),我忽略了这一点。我会更新我的答案...
  • 我删除了contain(),它就像一个魅力。但现在我收到 2 个我不明白的警告:Warning (4096): Argument 1 passed to Cake\Database\Expression\QueryExpression::_addConditions() must be of the type array, null given, called in /Applications/MAMP/htdocs/sites/giftbooking/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php on line 124 and defined [CORE/src/Database/Expression/QueryExpression.php, line 503]Warning (2): Invalid argument supplied for foreach() [CORE/src/Database/Expression/QueryExpression.php, line 509]
  • @ZbigniewLedwoń 是的,这是一个应该在下一个版本中修复的错误github.com/cakephp/cakephp/issues/7102如果你等不及可以使用dev-master,直到它可用。
  • @ZbigniewLedwoń 或者使用第二种解决方案,反正它可能更好。
【解决方案2】:

through 允许您提供要在连接表上使用的表实例的名称或实例本身。这使得自定义连接表键成为可能,并允许您自定义数据透视表的行为。

使用数组语法定义更具体的关系:

class StudentsTable extends Table
{

    public function initialize(array $config)
    {
        $this->belongsToMany('Courses', [
            'joinTable' => 'courses',
            'through' => 'CourseMemberships',
        ]);
    }
}

class CoursesTable extends Table
{

    public function initialize(array $config)
    {
        $this->belongsToMany('Students', [
            'joinTable' => 'students',
            'through' => 'CourseMemberships',
        ]);
    }
}

class CoursesMembershipsTable extends Table
{

    public function initialize(array $config)
    {
        $this->belongsTo('Students', [
            'foreignKey' => 'student_id',
            'joinType' => 'INNER',        // OR specify the type 
        ]);
        $this->belongsTo('Courses', [
            'foreignKey' => 'course_id',
            'joinType' => 'INNER',
        ]);
    }
}

确保您有表 coursesstudentscourse_memberships

现在运行代码:

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

好吧,如果你真的需要与HasMany Associations 相关的东西,我很害怕。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多