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:
CarbonPeriod
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:
$orderInfo['orders']->sum('total')
was used to calculate the total of all orders in the period$orderInfo['orders']->count()
was used to calculate the total of all orders in the period$orderInfo['orders']->where('status', OrderStatus::COMPLETED->value)->count()
was used to calculate the total of completed orders in the period$orderInfo['orders']->where('status', OrderStatus::PENDING->value)->count()
was used to calculate the total of pending orders in the periodThis 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).