次の関係を持つ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
を返すにはどうすればよいですか?
これはより良い解決策だと思います。 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
句を記述すると、見栄えがよくなります。外部で書き込むのと同じように機能します(私が間違っていない場合)、クエリをリファクタリングすると、外部で関係条件を忘れずに簡単に関係全体を削除できます。
以下のように使用してください。
$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();
これを使って:
_$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()
メソッドのパラメーター値として使用します。