I want to create product filter with Eloquent.
I start like this
$query = Product::whereHas('variants')
->with('variants')
->with('reviews')
$query = $this->addOrderConstraints($request, $query);
$products = $query->paginate(20);
Where
private function addOrderConstraints($request, $query)
{
$order = $request->input('sort');
if ($order === 'new') {
$query->orderBy('products.created_at', 'DESC');
}
if ($order === 'price') {
$query->orderBy('variants.price', 'ASC');
}
return $query;
}
However, that doesn't work, cause Eloquent is performing this query like this (information from Laravel DebugBar)
select count(*) as aggregate from `products` where exists
(select * from `variants` where `products`.`id` = `variants`.`product_id`)
select * from `products` where exists
(select * from `variants` where `products`.`id` = `variants`.`product_id`)
select * from `variants` where `variants`.`product_id` in ('29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48')
And so on
So when I try to use sorting by price it just obvious error
Unknown column 'variants.price' in 'order clause' (SQL: select * from
`products` where exists (select * from `variants` where `products`.`id` =
variants
.product_id
) order by variants
.price
asc limit 20 offset 0)
So is it possible to perform relationship ordering with Eloquent or not?
This will sort the subquery. Not the "first query (the product query)".
Basically, your subquery will be:
select * from variants where product_id in (....) order by price
, and that is not what you want, right?
<?php
// ...
$order = $request->sort;
$products = Product::whereHas('variants')->with(['reviews', 'variants' => function($query) use ($order) {
if ($order == 'price') {
$query->orderBy('price');
}
}])->paginate(20);
If you want to sort product +/or variant you need to use join.
$query = Product::select([
'products.*',
'variants.price',
'variants.product_id'
])->join('variants', 'products.id', '=', 'variants.product_id');
if ($order == 'new') {
$query->orderBy('products.created_at', 'DESC');
} else if ($order == 'price') {
$query->orderBy('variants.price');
}
return $query->paginate(20);
If you want to sort product and variants, you don't need joins, because you'll won't have the related model loaded (like $product->variants
), just all the fields of the variants table.
To sort models by related submodels, we can use Eloquent - Subquery Ordering.
To order the whole model by a related model, and NOT the related model itself, we can do it like this:
return Product::with('variants')->orderBy(
Variants::select('price')
// This can vary depending on the relationship
->whereColumn('variant_id', 'variants.id')
->orderBy('price')
->limit(1)
)->get();
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