【问题标题】:How to filter by conditions for associated models?如何按条件过滤关联模型?
【发布时间】:2015-01-04 02:49:47
【问题描述】:

我有一个关于用户和联系人的 belongsToMany 关联。

我想查找给定用户的联系人。 我需要类似的东西

$this->Contacts->find()->contain(['Users' => ['Users.id' => 1]]);

这本食谱谈到了给包含条件、自定义查找器方法和通过关联键唱歌,但我不知道如何将这些放在一起。

【问题讨论】:

    标签: cakephp associations conditional-statements cakephp-3.0 query-builder


    【解决方案1】:

    使用 Query::matching() 或 Query::innerJoinWith()

    Contacts 表中查询时,您要查找的是Query::matching()Query::innerJoinWith(),而不是(仅)Query::contain()

    请注意,通常首选innerJoinWith() 以避免严格分组的问题,因为matching() 会将关联的字段添加到选择列表中,这可能会导致问题,因为它们通常不依赖于功能。

    Cookbook > Database Access & ORM > Query Builder > Filtering by Associated Data

    这是一个使用您的表格的示例:

    $this->Contacts
        ->find()
        ->innerJoinWith('Users', function(\Cake\ORM\Query $q) {
            return $q->where(['Users.id' => 1]);
        });
    

    这将自动将所需的连接 + 条件添加到生成的查询中,以便仅检索与至少一个 ID 为 1 的用户相关联的联系人。

    定位连接表

    如果您需要通过hasManybelongsTo 手动设置多对多关联,您可以直接定位连接表:

    $this->Contacts
        ->find()
        ->innerJoinWith('ContactsUsers', function(\Cake\ORM\Query $q) {
            return $q->where(['ContactsUsers.user_id' => 1]);
        });
    

    包括容器

    如果您真的希望在结果中也返回所有关联,那么请继续使用 contain()

    $this->Contacts
        ->find()
        ->contain('Users')
        ->innerJoinWith('Users', function(\Cake\ORM\Query $q) {
            return $q->where(['Users.id' => 1]);
        });
    

    这将包含属于某个联系人的所有用户。

    限制收容

    如果您有多个匹配项,并且您只想包含这些匹配项,那么您也必须过滤包含。在此示例中,它没有多大意义,因为只有一个匹配项,但在其他情况下它可能很有用,例如,如果您想匹配具有活动用户的所有联系人,并检索仅包括活跃的关联用户:

    $this->Contacts
        ->find()
        ->contain(['Users' => function(\Cake\ORM\Query $q) {
            return $q->where(['Users.active' => true]);
        }])
        ->innerJoinWith('Users', function(\Cake\ORM\Query $q) {
            return $q->where(['Users.active' => true]);
        })
        ->group('Contacts.id');
    

    鉴于可能存在重复,即单个联系人的多个活动用户,您可能需要相应地对事物进行分组,以避免检索重复的联系人记录。

    深度关联

    您还可以通过使用Query::contain() 中已知的点表示路径语法来定位更深层次的关联。例如,假设您有一个 Users hasOne Profiles 关联,并且您只想匹配那些想要接收通知的用户,这可能看起来像这样:

    ->innerJoinWith('Users.Profiles', function(\Cake\ORM\Query $q) {
        return $q->where(['Profiles.receive_notifications' => true]);
    })
    

    这将自动创建所有必需的附加连接。

    改为从另一个表中选择

    通过这些关联和您的简单要求,您还可以轻松地从另一端查询,即通过Users 表并仅使用Query::contain() 来包含关联的联系人,例如

    $this->Users
        ->find()
        ->contain('Contacts')
        ->where([
            'Users.id' => 1
        ])
        ->first();
    

    然后可以在实体contacts 属性中找到所有联系人。

    【讨论】:

    • 谢谢,不知怎的我错过了这部分。
    • 谢谢。昨天偶然我发现了另一种方法来执行您在更新中包含的内容。 $this->Contacts->find()->contain(['users'])->matching('Users', function($q) { return $q->where(['Users.id' => 1 ]); });见:github.com/cakephp/cakephp/issues/5109
    • @rrd 啊,我应该更仔细地阅读这个问题,乍一看,我认为两者都不会给你想要的结果。现在有人需要找出套管技巧是否与更“复杂”的附加关联解决方法一样可行:)
    • 当条件为 return $q->where(['ContactsUsers.user_id IN' =>[1,2,3]]); 时,我得到重复的结果。请让我知道如何获得独特的结果?或者请在这里回复stackoverflow.com/questions/46909779/…@ndm
    • @SumonSarker 检查链接文档部分下的警告框,您必须执行distinct select,或相应地应用grouping(均使用主键)。
    【解决方案2】:

    虽然@ndm 的回答让我遇到了类似问题,但解决方案需要稍作调整。 问题是让用户通过 joinTable 上的数据过滤,添加匹配没有返回用户。所以这就是我最终的结果,希望它对某人有所帮助。

    $this->Contacts
    ->find()
    ->contain('Users', function(\Cake\ORM\Query $q) {
        return $q->matching('ContactsUsers', function(\Cake\ORM\Query $q) {
            return $q->where(['ContactsUsers.some_field' => 1]);
        }
    });
    

    这让我与关联表中 some_field 设置为 1 的用户联系。还是我过于复杂了,有更好的解决方案?

    【讨论】:

      猜你喜欢
      • 2013-12-07
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多