How to Use GroupBy with Pagination in Laravel 12 (Complete Tutorial)
When working with large datasets in Laravel applications, grouping and paginating results efficiently can be challenging. The GroupBy function helps you organize records based on specific columns, while pagination keeps your results clean and manageable.
In this tutorial, we’ll explore how to combine GroupBy with pagination in Laravel 12, using real-world examples, optimized queries, and clean Eloquent syntax.
The groupBy() method in Laravel (based on SQL’s GROUP BY) allows you to aggregate data—like counting orders by customers, listing posts by categories, or summarizing transactions by date.
Pagination ensures performance and usability, especially when you’re working with hundreds or thousands of grouped results.
Example use cases:
Before diving into the code, make sure your environment is ready.
composer create-project laravel/laravel laravel12-groupby-demo
cd laravel12-groupby-demo
php artisan serve
Once your Laravel app is running, set up a database connection inside your .env file:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=groupby_demo
DB_USERNAME=root
DB_PASSWORD=
Run migrations:
php artisan migrate
php artisan migrate
In the Order.php model, make sure your fillable fields are defined:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'total_amount',
];
}
You can use Laravel seeders to generate sample data:
php artisan make:seeder OrderSeeder
Edit database/seeders/OrderSeeder.php:
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Order;
class OrderSeeder extends Seeder
{
public function run()
{
Order::factory()->count(50)->create();
}
}
Then run:
php artisan db:seed --class=OrderSeeder
Here’s where the magic happens.
Open your OrderController.php and add the following code:
namespace App\Http\Controllers;
use App\Models\Order;
use Illuminate\Support\Facades\DB;
class OrderController extends Controller
{
public function index()
{
$orders = DB::table('orders')
->select('user_id', DB::raw('COUNT(*) as total_orders'), DB::raw('SUM(total_amount) as total_spent'))
->groupBy('user_id')
->paginate(10); // 10 groups per page
return view('orders.index', compact('orders'));
}
}
Create a view file at: resources/views/orders/index.blade.php
Laravel 12 GroupBy with Pagination
<div class="container">
<h2 class="mb-4 text-center">Orders Grouped by User</h2>
@foreach($orders as $order)
@endforeach
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>User ID</th>
<th>Total Orders</th>
<th>Total Amount Spent</th>
</tr>
</thead>
<tbody><tr>
<td>{{ $order->user_id }}</td>
<td>{{ $order->total_orders }}</td>
<td>${{ number_format($order->total_spent, 2) }}</td>
</tr></tbody>
</table>
<div class="d-flex justify-content-center">
{!! $orders->links() !!}
</div>
</div>
Add this line in routes/web.php:
use App\Http\Controllers\OrderController;
Route::get('/orders', [OrderController::class, 'index']);
Now visit:
👉 http://127.0.0.1:8000/orders
You’ll see all your grouped user orders neatly paginated.
If you prefer Eloquent for cleaner syntax:
$orders = Order::select('user_id', DB::raw('COUNT(*) as total_orders'), DB::raw('SUM(total_amount) as total_spent'))
->groupBy('user_id')
->paginate(10);
Same output — just more “Laravel-ish” and readable.
If you use ->get() instead of ->paginate(), you’ll fetch all grouped results at once.
That works fine for small tables, but it’s inefficient for large data sets.
Always use pagination for better memory optimization and performance.
Let’s say each order belongs to a user (via belongsTo relationship):
public function user()
{
return $this->belongsTo(User::class);
}
You can group and fetch related user names like this:
$orders = Order::with('user:id,name')
->select('user_id', DB::raw('SUM(total_amount) as total_spent'))
->groupBy('user_id')
->paginate(10);
Then in your Blade:
{{ $order->user->name }}
${{ $order->total_spent }}
Combining GroupBy with pagination in Laravel 12 allows you to create powerful, scalable dashboards and reports without compromising performance.
It’s especially useful in:
Laravel’s fluent syntax and pagination features make it simple to build efficient queries while keeping your code elegant and easy to maintain.
GroupBy in Laravel helps organize records based on one or more columns, similar to SQL’s GROUP BY. It’s commonly used for summarizing data such as totals, counts, or averages.
Yes, Laravel supports pagination on grouped queries using paginate() with both Query Builder and Eloquent. It allows you to manage large grouped datasets efficiently.
DB::table('orders')
->select('user_id', DB::raw('COUNT(*) as total_orders'))
->groupBy('user_id')
->paginate(10);
Yes. Laravel’s Query Builder and Eloquent ORM support joins, aggregates, and groupBy together — as long as your selected fields are compatible with SQL group rules.