Files
SkinbaseNova/app/Services/Sitemaps/SitemapBuildService.php

143 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Sitemaps;
use App\Services\Sitemaps\Builders\GoogleNewsSitemapBuilder;
final class SitemapBuildService
{
public function __construct(
private readonly SitemapCacheService $cache,
private readonly SitemapIndexService $index,
private readonly SitemapRegistry $registry,
private readonly SitemapShardService $shards,
private readonly SitemapXmlRenderer $renderer,
) {
}
/**
* @return array{content: string, source: string, type: string, url_count: int, shard_count: int, name: string}
*/
public function buildIndex(bool $force = false, bool $persist = true, ?array $families = null): array
{
$built = $this->cache->remember(
SitemapCacheService::INDEX_DOCUMENT,
fn (): string => $this->renderer->renderIndex($this->index->items($families)),
$force,
$persist,
);
return $built + [
'type' => SitemapTarget::TYPE_INDEX,
'url_count' => count($this->index->items($families)),
'shard_count' => 0,
'name' => SitemapCacheService::INDEX_DOCUMENT,
];
}
/**
* @return array{content: string, source: string, type: string, url_count: int, shard_count: int, name: string}|null
*/
public function buildNamed(string $name, bool $force = false, bool $persist = true): ?array
{
$target = $this->shards->resolve($this->registry, $name);
if ($target === null) {
return null;
}
$built = $this->cache->remember(
$target->documentName,
fn (): string => $this->renderTarget($target),
$force,
$persist,
);
return $built + [
'type' => $target->type,
'url_count' => $this->urlCount($target),
'shard_count' => $target->totalShards,
'name' => $target->documentName,
];
}
/**
* @return list<string>
*/
public function documentNamesForFamily(string $family, bool $includeCompatibilityIndex = true): array
{
$builder = $this->registry->get($family);
if ($builder === null) {
return [];
}
$names = $this->shards->canonicalDocumentNamesForBuilder($builder);
if ($includeCompatibilityIndex && $builder instanceof ShardableSitemapBuilder && $this->shards->shardCount($builder) > 1) {
array_unshift($names, $builder->name());
foreach (range(1, $this->shards->shardCount($builder)) as $shard) {
array_unshift($names, $builder->name() . '-' . $shard);
}
}
return array_values(array_unique($names));
}
/**
* @return list<string>
*/
public function canonicalDocumentNamesForFamily(string $family): array
{
$builder = $this->registry->get($family);
if ($builder === null) {
return [];
}
return $this->shards->canonicalDocumentNamesForBuilder($builder);
}
/**
* @return list<string>
*/
public function enabledFamilies(): array
{
return array_values(array_filter(
(array) config('sitemaps.enabled', []),
fn (mixed $name): bool => is_string($name) && $this->registry->get($name) !== null,
));
}
private function renderTarget(SitemapTarget $target): string
{
if ($target->type === SitemapTarget::TYPE_INDEX) {
return $this->renderer->renderIndex($this->index->itemsForBuilder($target->builder));
}
if ($target->builder instanceof GoogleNewsSitemapBuilder || $target->type === SitemapTarget::TYPE_GOOGLE_NEWS) {
return $this->renderer->renderGoogleNewsUrlset($target->builder->items());
}
if ($target->builder instanceof ShardableSitemapBuilder && $target->shardNumber !== null) {
return $this->renderer->renderUrlset($target->builder->itemsForShard($target->shardNumber));
}
return $this->renderer->renderUrlset($target->builder->items());
}
private function urlCount(SitemapTarget $target): int
{
if ($target->type === SitemapTarget::TYPE_INDEX) {
return count($this->index->itemsForBuilder($target->builder));
}
if ($target->builder instanceof ShardableSitemapBuilder && $target->shardNumber !== null) {
return count($target->builder->itemsForShard($target->shardNumber));
}
return count($target->builder->items());
}
}