web-dev-qa-db-ja.com

関連するモデルの条件でフィルタリングするにはどうすればよいですか?

ユーザーと連絡先にbelongsToManyアソシエーションがあります。

指定されたユーザーの連絡先を検索したいのですが。私は次のようなものが必要になります

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

クックブックには、含める条件の指定、カスタムFinderメソッド、および関連付けキーによる歌唱について説明されていますが、これらを組み合わせる方法がわかりませんでした。

12
rrd

Query :: matching()またはQuery :: innerJoinWith()を使用します

Contactsテーブルからクエリを実行する場合、探しているのはQuery::matching()またはQuery::innerJoinWith()であり、Query::contain()だけではありません。

クックブック>データベースアクセスとORM>クエリビルダー>関連データによるフィルタリング を参照してください。

テーブルを使用した例を次に示します。

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

これにより、生成されたクエリに必要な結合+条件が自動的に追加されるため、IDが_1_の少なくとも1人のユーザーに関連付けられている連絡先のみが取得されます。

結合テーブルをターゲットにする

hasManyおよびbelongsToを介して多対多の関連付けを手動で設定する場合は、結合テーブルを直接ターゲットにすることができます。

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

封じ込めを含む

実際にすべての関連付けを結果に返したい場合は、contain()も使い続けてください。

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

これには、連絡先に属するすべてのユーザーが含まれます。

封じ込めの制限

複数の一致があり、それらの一致のみを含めたい場合は、包含もフィルタリングする必要があります。この例では、一致するものが1つしかないため、あまり意味がありませんが、他の状況では、たとえば、アクティブなユーザーがいるすべての連絡先を一致させて、以下を含む連絡先のみを取得する場合などに役立ちます。アクティブな関連ユーザー:

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

重複する可能性がある場合、つまり1つの連絡先に複数のアクティブユーザーがいる可能性がある場合は、重複する連絡先レコードを取得しないように、それに応じてグループ化することをお勧めします。

深い関係

Query::contain()で知られているドット表記のパス構文を使用して、その方法でより深い関連付けをターゲットにすることもできます。たとえば、_Users hasOne Profiles_の関連付けがあり、通知を受信したいユーザーのみに一致させたい場合、次のようになります。

_->matching('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プロパティで見つけることができます。

21
ndm

@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に設定されているユーザーとの連絡先が表示されました。それとも私は複雑すぎて、より良い解決策がありますか?

0
Yevgeniy