I have three tables with the following relations,
------- 1 0..* ------------
|Product|-------------|Availability|
------- ------------
1 |
|
1 |
--------
|MetaData|
--------
my raw sql looks like this
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;
That is, find each Product
with a MetaData.published_state
equal to 1
and with no Availability
such that Availability.start
more than 7 days from now()
.
I'm trying to accomplish the same using ActiveRecord
methods, using something like the following,
$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();
however, this is returning no results. Using Connection::createCommand()
to run the raw sql returns the rows I'd expect so there is no issue with the data.
I suspect the issue is being caused by the join
conditions and the where
conditions 'bleeding' into each other; both join and where being applied to either the joining or the where rather than separately.
How can I output the actual sql query being run? this is in an action being called from a console controller.
How can I alter my code to return the desired Products
?
I believe this one is better solution. Instead of using Raw queries like leftJoin
you should complement your joinWith
relations with andOnCondition
(which adds needed where conditions into your join statement).
$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();
In addition it looks cleaner when you write where
clauses inside relations. It works the same as writing it outside (if I'm not wrong), but when refactoring your query, you can easily delete the whole relation without forgetting relation conditions outside.
Just use like below condition.
$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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With