戏里戏外

Laravel 中模型的自定义集合

2024-11-27#Laravel#Eloquent

Laravel 中的集合可以将其视为一个增强版的数组,它在 Laravel 生态系统中非常有用。

代码示例

集合在 Laravel 生态系统中非常强大,但在某些场景下,执行特定操作的逻辑可能变得复杂和冗长,这时就需要考虑抽象这些操作。

一起来看一个 MoonGuard 代码库中的实际案例,这里需要检查每个站点的状态。

以下是 CheckUptimeCommand 类的示例:

<?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');
  }
}

创建自定义集合

我们可以创建一个 SiteCollection 类来封装这些逻辑:

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

在模型中使用自定义集合

要让 Site 模型使用这个集合,需要重写模型的 newCollection() 方法。

<?php

namespace Taecontrol\MoonGuard\Models;

use Taecontrol\MoonGuard\Collections\SiteCollection;

#[CollectedBy(SiteCollection::class)]
class Site extends Model
{
  // ...
}
<?php

namespace Taecontrol\MoonGuard\Models;

use Taecontrol\MoonGuard\Collections\SiteCollection;

class Site extends Model
{
  // ...
  public function newCollection(array $models = []): SiteCollection
  {
    return new SiteCollection($models);
  }
}

更新命令类

最后更新 UptimeCheckCommand 命令以使用 SiteCollection:

<?php

namespace Taecontrol\MoonGuard\Console\Commands;

use Illuminate\Console\Command;
use Taecontrol\MoonGuard\Repositories\SiteRepository;

class CheckUptimeCommand extends Command
{
  protected $signature = 'check:uptime';
  protected $description = 'Check uptime for all registered sites';
  public function handle()
  {
    // ...
    $this->info('[Uptime] Starting check...');
    SiteRepository::query()
      ->whereUptimeCheckEnabled()
      ->whereIsNotOnMaintenance()
      ->with('uptimeCheck')
      ->get()
     ->checkUptime();
    $this->info('[Uptime] Uptime checked');
  }
}

自定义集合允许以实用的方式自定义集合的行为,更重要的是它通过抽象和封装逻辑帮助我们提高代码的可读性和一致性。