web-dev-qa-db-ja.com

Yii2-複数の条件での左結合

次の関係を持つ3つのテーブルがあります。

_  ------- 1        0..* ------------
 |Product|-------------|Availability|
  -------               ------------
    1 |
      |
    1 |
  --------
 |MetaData|
  --------
_

私の生のSQLはこのようになります

_SELECT p.ID FROM product p 
LEFT JOIN availability a ON a.productID=p.ID 
          AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)
LEFT JOIN meta_data m ON m.ID=p.meta_dataID
WHERE a.ID IS NULL
AND m.published_state=1;
_

つまり、_MetaData.published_state_が_1_と等しく、Productがなく、_Availability.start_がnow()から7日を超えるようなAvailabilityをそれぞれ検索します。

次のようなものを使用して、ActiveRecordメソッドを使用して同じことを達成しようとしています、

_$products = Product::find()
            ->joinWith('metaData')
            ->joinWith('availability')
            ->onCondition(['>=', 'availability.start', strtotime('+7 days')])
            ->where(['is', 'availability.ID', NULL])
            ->andWhere(['=', 'meta_data.published_state', 1])
            ->all();
_

ただし、これは結果を返しません。 Connection::createCommand()を使用して生のsqlを実行すると、予想される行が返されるため、データに問題はありません。

この問題は、join条件とwhere条件が互いに「混じり合っている」ために引き起こされているのではないかと思います。 joinとwhereの両方が、個別ではなく、joinまたはwhereのいずれかに適用されます。

実行中の実際のSQLクエリを出力するにはどうすればよいですか?これは、コンソールコントローラから呼び出されているアクションです。

コードを変更して目的のProductsを返すにはどうすればよいですか?

5
Pedro del Sol

これはより良い解決策だと思います。 leftJoinのようなRawクエリを使用する代わりに、joinWith関係をandOnConditionで補完する必要があります(結合ステートメントに必要なwhere条件を追加します)。

$products = Product::find()
    ->joinWith(['metaData' => function (ActiveQuery $query) {
        return $query
            ->andWhere(['=', 'meta_data.published_state', 1]);
    }])
    ->joinWith(['availability' => function (ActiveQuery $query) {
        return $query
            ->andOnCondition(['>=', 'availability.start', strtotime('+7 days')])
            ->andWhere(['IS', 'availability.ID', NULL]);
    }])
    ->all();

さらに、リレーション内にwhere句を記述すると、見栄えがよくなります。外部で書き込むのと同じように機能します(私が間違っていない場合)、クエリをリファクタリングすると、外部で関係条件を忘れずに簡単に関係全体を削除できます。

13
Yerke

以下のように使用してください。

$query = Product::find()
 -> leftJoin('availability', 'availability.productID=product.ID  AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)')
 ->leftJoin('meta_data', 'meta_data.ID=product.meta_dataID')
 ->where(['is', 'availability.ID', NULL])
 ->andWhere(['=', 'meta_data.published_state', 1])
 ->all();
6
Ishan Shah

これを使って:

_$sql = 'SELECT p.ID FROM product p 
LEFT JOIN availability a ON a.productID=p.ID 
          AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)
LEFT JOIN meta_data m ON m.ID=p.meta_dataID
WHERE a.ID IS NULL
AND m.published_state=1';

$products = Product::findBySql($sql);
_

Yii Active Recordには findBySql($sql) メソッドがあり、生のSQLクエリを使用してデータベースからデータを実行および取得できます。 Yiiのクエリメソッドと混同されたとき、またはクエリがYii.

したがって、基本的に、上記のコードブロックでは、生のSQLクエリを_$sql_という変数に配置し、それをfindBySql()メソッドのパラメーター値として使用します。

1
Krisan Paulino