Skip to main content

Custom Collections in Laravel

· 3 min read
Ronald Pereira

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()])
);
}
}

note

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 😉.