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).
Unfortunately, the code example given above does not reflect real information, trivially, the number of displayed orders for a certain period does not correspond to the number of orders in the database for the same period.
Below is a simpler and faster code that will make only 2 requests to the database, and most importantly will return true information
Thank you, we will take a look into this soon and update the examples. Your previous comment showed us that we had a problem in our testing, so we will update this course with tests and corrections!