Back to Course |
Laravel GroupBy: Practical Examples

Example 4. Extensive Collection Usage

Collections are a powerful tool that can be used in many ways. For example, this table would not be easy to do with a database query:

Here we used collections to:

  • Generate periods using CarbonPeriod
  • Load data from the database in those periods
  • On display, we used collection methods to get totals for each period
  • We also used collection methods to filter specific data counts

Here's how we did it:

app/Http/Controllers/OrderReportsController.php

use App\Enum\OrderStatus;
use App\Models\Order;
use Carbon\CarbonPeriod;
 
// ...
 
public function __invoke()
{
// Empty collection
$orders = collect([]);
 
// Generate periods for six months in weekly intervals
$periodInformation = CarbonPeriod::create(now()->subMonths(6)->startOfWeek(), '1 week', now()->endOfWeek());
 
foreach ($periodInformation as $period) {
$weekStart = $period->format('Y-m-d');
$weekEnd = $period->copy()->endOfWeek()->format('Y-m-d');
 
// Pushed a period into our empty collection
$orders->push([
// Set the week start and end
'week' => $weekStart . ' - ' . $weekEnd,
// Loaded orders for the week
'orders' => Order::query()
->whereBetween('order_time', [$weekStart, $weekEnd])
->withCount('products')
->with(['user'])
->where('status', '!=', OrderStatus::CANCELLED->value)
->orderBy('status')
->get()
]);
}
 
// Filtered out weeks that have no orders
$orders = $orders->filter(function ($order) {
return $order['orders']->count() > 0;
});
 
return view('orders.reports', [
'orders' => $orders
]);
}

In our view, we expect to have a collection of periods, each containing a week and orders for that week:

resources/views/orders/reports.blade.php

@foreach($orders as $orderInfo)
<h2 class="text-2xl font-bold mb-2">{{ $orderInfo['week'] }}</h2>
 
<div class="grid grid-cols-4 gap-4 mt-4">
<div class="border p-4">
<h3 class="text-xl">Total value</h3>
<span>${{ number_format($orderInfo['orders']->sum('total'), 2) }}</span>
</div>
<div class="border p-4">
<h3 class="text-xl">Total orders</h3>
<span>{{ $orderInfo['orders']->count() }}</span>
</div>
<div class="border p-4">
<h3 class="text-xl">Completed Orders</h3>
<span>{{ $orderInfo['orders']->where('status', OrderStatus::COMPLETED->value)->count() }}</span>
</div>
<div class="border p-4">
<h3 class="text-xl">Pending Orders</h3>
<span>{{ $orderInfo['orders']->where('status', OrderStatus::PENDING->value)->count() }}</span>
</div>
</div>
 
<table class="table-auto w-full mt-4 mb-4">
<thead>
<tr>
<th class="px-4 py-2">#</th>
<th class="px-4 py-2">User</th>
<th class="px-4 py-2">Order time</th>
<th class="px-4 py-2">Delivery time</th>
<th class="px-4 py-2">Products Amount</th>
<th class="px-4 py-2">Total</th>
<th class="px-4 py-2">Status</th>
</tr>
</thead>
<tbody>
@foreach($orderInfo['orders'] as $order)
<tr>
<td class="border px-4 py-2">{{ $order->id }}</td>
<td class="border px-4 py-2">{{ $order->user->name }}</td>
<td class="border px-4 py-2">{{ $order->order_time }}</td>
<td class="border px-4 py-2">{{ $order->delivery_time }}</td>
<td class="border px-4 py-2">{{ $order->products_count }}</td>
<td class="border px-4 py-2">${{ number_format($order->total, 2) }}</td>
<td class="border px-4 py-2">{{ $order->status }}</td>
</tr>
@endforeach
</tbody>
</table>
@endforeach

Here are a few things to note here:

  • A collection method $orderInfo['orders']->sum('total') was used to calculate the total of all orders in the period
  • A collection method $orderInfo['orders']->count() was used to calculate the total of all orders in the period
  • A collection method $orderInfo['orders']->where('status', OrderStatus::COMPLETED->value)->count() was used to calculate the total of completed orders in the period
  • A collection method $orderInfo['orders']->where('status', OrderStatus::PENDING->value)->count() was used to calculate the total of pending orders in the period

This allowed us to transform our Database data without writing complex SQL queries. We modified the data in the collection and then displayed it in the view to get the desired result. Of course, the drawback of this is quite a few SQL queries:

So you have to find the balance between collection methods and SQL queries. In this case, only some things could be done with SQL queries simply (for example, we could not group by week and still list all the orders for that week).