103 lines
2.8 KiB
PHP
103 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Seo;
|
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
final class BreadcrumbTrail
|
|
{
|
|
/**
|
|
* @param iterable<mixed>|Collection<int, mixed>|null $breadcrumbs
|
|
* @return list<array{name: string, url: string}>
|
|
*/
|
|
public static function normalize(iterable|Collection|null $breadcrumbs): array
|
|
{
|
|
$items = collect($breadcrumbs instanceof Collection ? $breadcrumbs->all() : ($breadcrumbs ?? []))
|
|
->map(fn (mixed $crumb): ?array => self::mapCrumb($crumb))
|
|
->filter(fn (?array $crumb): bool => is_array($crumb) && $crumb['name'] !== '' && $crumb['url'] !== '')
|
|
->values();
|
|
|
|
$home = [
|
|
'name' => 'Home',
|
|
'url' => self::absoluteUrl('/'),
|
|
];
|
|
|
|
$normalized = [];
|
|
$seen = [];
|
|
|
|
foreach ($items as $crumb) {
|
|
$name = trim((string) ($crumb['name'] ?? ''));
|
|
$url = self::absoluteUrl((string) ($crumb['url'] ?? ''));
|
|
|
|
if ($name === '' || $url === '') {
|
|
continue;
|
|
}
|
|
|
|
if (self::isHome($name, $url)) {
|
|
$name = $home['name'];
|
|
$url = $home['url'];
|
|
}
|
|
|
|
$key = strtolower($name) . '|' . rtrim(strtolower($url), '/');
|
|
if (isset($seen[$key])) {
|
|
continue;
|
|
}
|
|
|
|
$seen[$key] = true;
|
|
$normalized[] = ['name' => $name, 'url' => $url];
|
|
}
|
|
|
|
if ($normalized === [] || ! self::isHome($normalized[0]['name'], $normalized[0]['url'])) {
|
|
array_unshift($normalized, $home);
|
|
}
|
|
|
|
return array_values($normalized);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $crumb
|
|
* @return array{name: string, url: string}|null
|
|
*/
|
|
private static function mapCrumb(mixed $crumb): ?array
|
|
{
|
|
if (is_array($crumb)) {
|
|
return [
|
|
'name' => trim((string) ($crumb['name'] ?? '')),
|
|
'url' => trim((string) ($crumb['url'] ?? '')),
|
|
];
|
|
}
|
|
|
|
if (is_object($crumb)) {
|
|
return [
|
|
'name' => trim((string) ($crumb->name ?? '')),
|
|
'url' => trim((string) ($crumb->url ?? '')),
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static function isHome(string $name, string $url): bool
|
|
{
|
|
$normalizedUrl = rtrim(strtolower($url), '/');
|
|
$homeUrl = rtrim(strtolower(self::absoluteUrl('/')), '/');
|
|
|
|
return strtolower($name) === 'home' || $normalizedUrl === $homeUrl;
|
|
}
|
|
|
|
private static function absoluteUrl(string $url): string
|
|
{
|
|
$trimmed = trim($url);
|
|
if ($trimmed === '') {
|
|
return '';
|
|
}
|
|
|
|
if (preg_match('/^https?:\/\//i', $trimmed) === 1) {
|
|
return $trimmed;
|
|
}
|
|
|
|
return url($trimmed);
|
|
}
|
|
} |