Collections in Laravel allow us to represent a set of objects and manipulate them in multiple ways. We could say that they are like an array with batteries, which makes them really useful when working with Laravel, but have you ever wonder how to make your own collection?
Custom Collections, Why?
Collections are very powerful in Laravel ecosystem, but there are scenarios and contexts where the logic to perform certain operations can become complex and extensive. This leads us to consider abstracting those operations. We have a practical case inside MoonGuard codebase when we need to review the status of each site. Let's take a look at CheckUptimeCommand
class:
<?php
namespace Taecontrol\MoonGuard\Console\Commands;
use Illuminate\Console\Command;
use Taecontrol\MoonGuard\Models\Site;
use Taecontrol\MoonGuard\Contracts\MoonGuardSite;
class CheckUptimeCommand extends Command
{
protected $signature = 'check:uptime';
protected $description = 'Check uptime for all registered sites';
public function handle()
{
//...
$this->info('[Uptime] Starting check...');
/** @var Collection*/
$sites = SiteRepository::query()
->whereUptimeCheckEnabled()
->whereIsNotOnMaintenance()
->with('uptimeCheck')
->get();
$responses = Http::pool(...);
/** @var UptimeCheckService $uptimeCheckService */
$uptimeCheckService = app(UptimeCheckService::class);
$sites->each(
/** @throws InvalidPeriodException **/
fn (MoonGuardSite $site) => $uptimeCheckService->check($site, $responses[$site->url->__toString()])
);
$this->info('[Uptime] Uptime checked');
}
}
This class is a command class called CheckUptimeCommand
that, when used in the terminal, we will check the status of each registered site in MoonGuard through the service UptimeCheckService
and its function check(...)
.
While working on this project, we realized the set of instructions we were developing could be reused across other classes and collections of Site
, so we encapsulated this logic inside a SiteCollection
class with a method checkUptime()
. Let's continue with the creation of the custom collection SiteCollection
using the following code:
<?php
namespace Taecontrol\MoonGuard\Collections;
use Illuminate\Support\Facades\Http;
use Illuminate\Database\Eloquent\Collection;
use Taecontrol\MoonGuard\Contracts\MoonGuardSite;
use Taecontrol\MoonGuard\Services\UptimeCheckService;
use Taecontrol\MoonGuard\Exceptions\InvalidPeriodException;
use Taecontrol\MoonGuard\Services\SslCertificateCheckService;
class SiteCollection extends Collection
{
public function checkUptime(): void
{
/** @var array<string, Response> $responses */
$responses = Http::pool(...);
/** @var UptimeCheckService $uptimeCheckService */
$uptimeCheckService = app(UptimeCheckService::class);
$this->each(/**
* @throws InvalidPeriodException
*/ fn (MoonGuardSite $site) => $uptimeCheckService->check($site, $responses[$site->url->__toString()])
);
}
}
Any custom collection must extends from Illuminate\Database\Eloquent\Collection
or Illuminate\Support\Collection
.
To make our Site
model use this collection, we must implement the newCollection()
method:
<?php
namespace Taecontrol\Larastats\Models;
// ...
use Taecontrol\Larastats\Collections\SiteCollection;
class Site extends Model implements LarastatsSite
{
// ...
public function newCollection(array $models = []): SiteCollection
{
return new SiteCollection($models);
}
}
Finally, let's update UptimeCheckCommand
command to use our SiteCollection
:
<?php
namespace Taecontrol\MoonGuard\Console\Commands;
use Illuminate\Console\Command;
use Taecontrol\MoonGuard\Repositories\SiteRepository;
use Taecontrol\MoonGuard\Repositories\UptimeCheckRepository;
class CheckUptimeCommand extends Command
{
protected $signature = 'check:uptime';
protected $description = 'Check uptime for all registered sites';
public function handle()
{
if (! UptimeCheckRepository::isEnabled()) {
$this->info('[Uptime] This check is disabled. If you want to enable it, check the moonguard config file.');
return;
}
$this->info('[Uptime] Starting check...');
SiteRepository::query()
->whereUptimeCheckEnabled()
->whereIsNotOnMaintenance()
->with('uptimeCheck')
->get()
->checkUptime();
$this->info('[Uptime] Uptime checked');
}
}
In conclusion, custom collections allow us to customize the behavior of collections in a practical way. More importantly, it helps us improve the readability and consistency of our code by abstracting and encapsulating logic.
Discover the secrets behind our successful software with our book "MoonGuard: The Software Creator’s Journey" to learn how to create successful Laravel package from scratch! In addition to our website, we also maintain an active presence on Twitter (@moonguard_dev). By following us on Twitter, you'll be the first to know about any breaking news or important announcements regarding MoonGuard. So be sure to check us out and stay connected!
Thanks for reading, see you soon 😉.