Perform order by relationship field in Eloquent
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Title: Efficiently Perform Order by Relationship Fields in Eloquent
The world of modern web development is vast and ever‑changing. Among the popular frameworks, Laravel has gained a reputation for its simplicity, elasticity, and customization possibilities. One of the most important aspects of any application is how it handles and organizes data. This article will guide you through the process of creating a product filter using Eloquent ORM in Laravel, addressing an issue related to order by relationship fields during query execution.
The Code
$query = Product::whereHas('variants')
->with('variants')
->with('reviews');
$query = $this->addOrderConstraints($request, $query);
$products = $query->paginate(20);
In this example, we begin by retrieving all related models using a query builder. The product belongs to many variations and also has several reviews. To handle sorting or filtering based on relationships, we need to create custom methods for each sorting criteria. This is done with your own logic in the controller code, where you define specific order constraints based on the user's input:
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;
}
Note that we are using the with() method to eager load all related models instead of performing multiple database queries when retrieving the products.
Debugging Order Constraints
When testing these methods, you might encounter an error due to the order by relationship field. This happens because Eloquent performs a join query internally, and the order by clause is applied over the original table. For example: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')
To fix this, we need to apply the order constraints to each related model. We can achieve this with additional joins:
private function addOrderConstraints($request, $query) {
$order = $request->input('sort');
if ($order === 'new') {
$query->join('variants', function($join) {
$join->on('products.id', '=', 'variants.product_id');
})
->orderBy('variants.price', 'ASC')
->with('reviews')
->whereHas('reviews')
->orderBy('products.created_at', 'DESC');
}
if ($order === 'price') {
$query->join('variants', function($join) {
$join->on('products.id', '=', 'variants.product_id');
})
->orderBy('variants.price', 'ASC')
->with('reviews')
->whereHas('reviews');
}
return $query;
}
In this example, we join the products table with both variations and reviews and apply order constraints separately. This ensures that each related model is ordered correctly while maintaining a clear application logic.