135 lines
3.4 KiB
PHP
135 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Sitemaps\Builders;
|
|
|
|
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
|
use App\Services\Sitemaps\ShardableSitemapBuilder;
|
|
use App\Services\Sitemaps\SitemapUrl;
|
|
use DateTimeInterface;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
abstract class AbstractIdShardableSitemapBuilder extends AbstractSitemapBuilder implements ShardableSitemapBuilder
|
|
{
|
|
/**
|
|
* @return Builder<Model>
|
|
*/
|
|
abstract protected function query(): Builder;
|
|
|
|
abstract protected function shardConfigKey(): string;
|
|
|
|
abstract protected function mapRecord(Model $record): ?SitemapUrl;
|
|
|
|
public function items(): array
|
|
{
|
|
return $this->query()
|
|
->orderBy($this->idColumn())
|
|
->cursor()
|
|
->map(fn (Model $record): ?SitemapUrl => $this->mapRecord($record))
|
|
->filter()
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
public function lastModified(): ?DateTimeInterface
|
|
{
|
|
return $this->newest(...array_map(
|
|
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
|
$this->items(),
|
|
));
|
|
}
|
|
|
|
public function totalItems(): int
|
|
{
|
|
return (clone $this->query())->count();
|
|
}
|
|
|
|
public function shardSize(): int
|
|
{
|
|
return max(1, (int) \data_get(\config('sitemaps.shards', []), $this->shardConfigKey() . '.size', 10000));
|
|
}
|
|
|
|
public function itemsForShard(int $shard): array
|
|
{
|
|
$window = $this->shardWindow($shard);
|
|
|
|
if ($window === null) {
|
|
return [];
|
|
}
|
|
|
|
return $this->applyShardWindow($window['from'], $window['to'])
|
|
->get()
|
|
->map(fn (Model $record): ?SitemapUrl => $this->mapRecord($record))
|
|
->filter()
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
public function lastModifiedForShard(int $shard): ?DateTimeInterface
|
|
{
|
|
return $this->newest(...array_map(
|
|
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
|
$this->itemsForShard($shard),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return array{from: int, to: int}|null
|
|
*/
|
|
protected function shardWindow(int $shard): ?array
|
|
{
|
|
if ($shard < 1) {
|
|
return null;
|
|
}
|
|
|
|
$size = $this->shardSize();
|
|
$current = 0;
|
|
$from = null;
|
|
$to = null;
|
|
|
|
$windowQuery = (clone $this->query())
|
|
->setEagerLoads([])
|
|
->select([$this->idColumn()])
|
|
->orderBy($this->idColumn());
|
|
|
|
foreach ($windowQuery->cursor() as $record) {
|
|
$current++;
|
|
|
|
if ((int) ceil($current / $size) !== $shard) {
|
|
continue;
|
|
}
|
|
|
|
$recordId = (int) $record->getAttribute($this->idColumn());
|
|
$from ??= $recordId;
|
|
$to = $recordId;
|
|
}
|
|
|
|
if ($from === null || $to === null) {
|
|
return null;
|
|
}
|
|
|
|
return ['from' => $from, 'to' => $to];
|
|
}
|
|
|
|
/**
|
|
* @return Builder<Model>
|
|
*/
|
|
protected function applyShardWindow(int $from, int $to): Builder
|
|
{
|
|
return (clone $this->query())
|
|
->whereBetween($this->qualifiedIdColumn(), [$from, $to])
|
|
->orderBy($this->idColumn());
|
|
}
|
|
|
|
protected function idColumn(): string
|
|
{
|
|
return 'id';
|
|
}
|
|
|
|
protected function qualifiedIdColumn(): string
|
|
{
|
|
return $this->idColumn();
|
|
}
|
|
} |