Laravel group record by months and sum price
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Monthly Aggregation in Laravel: Grouping and Summing Order Prices
Hello there! As a senior developer, I often encounter scenarios where we need to transform raw database results into meaningful, aggregated collections—especially when preparing data for front-end visualization tools like DataTables. You are asking exactly the right question: how to efficiently group records by month and calculate the sum of prices in Laravel Eloquent?
The attempt you made using groupBy with a closure and date formatting is a common starting point, but as you discovered, it can often lead to unexpected results or complexity when dealing with database-level aggregation functions simultaneously. Let's dive into why that method sometimes falls short and explore the most robust, idiomatic ways to achieve this grouping in Laravel.
The Challenge with Direct Grouping
When you try to group by a calculated date string within an Eloquent query, you are asking the database to perform the grouping based on that string, which can be tricky if the underlying data structure isn't perfectly aligned, or if the database engine doesn't handle the aliasing exactly as expected across all platforms.
The goal is not just to group the rows, but to ensure we get a clean collection where each entry represents a unique month and its corresponding total price.
The Developer Solution: Leveraging Database Aggregation
The most reliable and performant way to handle this is to let the database do the heavy lifting for the aggregation (SUM) and then use Eloquent's grouping capabilities on the resulting aggregated set. We will combine DB::raw for calculation with proper date formatting.
Here is a comprehensive approach that ensures you get the desired collection structure, which is perfect for feeding into DataTables.
Step-by-Step Implementation
Assume you have an Order model with price (decimal/float) and created_at (datetime) columns.
1. Grouping by Year-Month
Instead of trying to group the full record set, we will select the year and month directly from the created_at column using database functions, which is much more efficient than relying on PHP date manipulation for grouping:
use Illuminate\Support\Facades\DB;
use App\Models\Order;
use Carbon\Carbon;
$monthlyTotals = Order::select(
DB::raw("DATE_FORMAT(created_at, '%Y-%m') as year_month"), // Format the date for grouping
DB::raw('SUM(price) as total_price') // Calculate the sum
)
->groupBy('year_month')
->orderBy('year_month', 'asc')
->get();
2. Transforming the Collection for DataTables
The result from the query above is a collection of objects, each containing the grouped month and the summed price. This structure is immediately ready for use in your DataTables setup:
// $monthlyTotals will look like this:
/*
[
{ "year_month": "2023-10", "total_price": 1500.75 },
{ "year_month": "2023-11", "total_price": 2100.50 },
// ... and so on
]
*/
If you need to present this data in a more human-readable format (e.g., displaying the actual month name instead of just '2023-10'), you can easily map over this collection in your controller or service layer:
$formattedData = $monthlyTotals->map(function ($item) {
// Use Carbon to parse the year_month string back into a date object for formatting
$date = Carbon::createFromFormat('Y-m', $item->year_month);
return [
'month' => $date->format('F Y'), // e.g., October 2023
'total_price' => $item->total_price,
];
});
Best Practices for Eloquent Aggregation
When dealing with complex aggregations like this, remember these principles:
- Let the Database Handle It: Whenever possible, use native database functions (
DATE_FORMAT,SUM,DATE_TRUNC) within yourDB::raw()calls. This pushes the computational load to the database engine, which is significantly faster than fetching all records and processing them in PHP memory. - Use Proper Indexing: Ensure that your date columns (
created_at) are properly indexed. This is crucial for speeding up any time-based grouping queries. - Eloquent as an Interface: While raw SQL is powerful, always aim to use Eloquent methods (like
with(),where()) for standard operations. For highly specific aggregations like this, combining the power of Eloquent models with the raw power of the Query Builder is a common and effective pattern in Laravel development.
By focusing on aggregation via DB::raw() and grouping by a database-friendly format (like YYYY-MM), you transform an ambiguous request into a highly efficient and scalable solution. Happy coding! If you are looking for deeper insights into optimizing your database interactions, exploring the official documentation at laravelcompany.com is always a great next step.