Wire admin studio SSR and search infrastructure

This commit is contained in:
2026-05-01 11:46:06 +02:00
parent 257b0dbef6
commit 18cea8b0f0
329 changed files with 197465 additions and 2741 deletions

View File

@@ -82,6 +82,8 @@ final class SeoDataBuilder
url: $attributes['og_url'] ?? null,
image: $attributes['og_image'] ?? null,
imageAlt: $attributes['og_image_alt'] ?? null,
imageWidth: isset($attributes['og_image_width']) ? (int) $attributes['og_image_width'] : null,
imageHeight: isset($attributes['og_image_height']) ? (int) $attributes['og_image_height'] : null,
);
$builder->twitter(
card: $attributes['twitter_card'] ?? null,
@@ -121,7 +123,7 @@ final class SeoDataBuilder
public function canonical(?string $canonical): self
{
$this->canonical = $this->absoluteUrl($canonical);
$this->canonical = $this->absolutePageUrl($canonical);
return $this;
}
@@ -141,13 +143,13 @@ final class SeoDataBuilder
public function prev(?string $prev): self
{
$this->prev = $this->absoluteUrl($prev);
$this->prev = $this->absolutePageUrl($prev);
return $this;
}
public function next(?string $next): self
{
$this->next = $this->absoluteUrl($next);
$this->next = $this->absolutePageUrl($next);
return $this;
}
@@ -168,7 +170,7 @@ final class SeoDataBuilder
return $this;
}
public function og(?string $type = null, ?string $title = null, ?string $description = null, ?string $url = null, ?string $image = null, ?string $imageAlt = null): self
public function og(?string $type = null, ?string $title = null, ?string $description = null, ?string $url = null, ?string $image = null, ?string $imageAlt = null, ?int $imageWidth = null, ?int $imageHeight = null): self
{
$this->og = array_filter([
'type' => $this->clean($type),
@@ -177,6 +179,8 @@ final class SeoDataBuilder
'url' => $this->absoluteUrl($url),
'image' => $this->absoluteUrl($image),
'image_alt' => $this->clean($imageAlt),
'image_width' => $imageWidth,
'image_height' => $imageHeight,
], fn (mixed $value): bool => $value !== null && $value !== '');
return $this;
@@ -199,7 +203,7 @@ final class SeoDataBuilder
$title = $this->title ?: (string) config('seo.default_title', 'Skinbase');
$description = $this->description ?: (string) config('seo.default_description', 'Skinbase');
$robots = $this->robots ?: (string) config('seo.default_robots', 'index,follow');
$canonical = $this->canonical ?: url()->current();
$canonical = $this->canonical ?: $this->defaultCanonicalUrl();
$fallbackImage = $this->absoluteUrl((string) config('seo.fallback_image_path', '/gfx/skinbase_back_001.webp'));
$image = $this->og['image'] ?? $this->twitter['image'] ?? $fallbackImage;
@@ -246,6 +250,8 @@ final class SeoDataBuilder
'og_url' => $ogUrl,
'og_image' => $image,
'og_image_alt' => $this->og['image_alt'] ?? null,
'og_image_width' => $this->og['image_width'] ?? null,
'og_image_height' => $this->og['image_height'] ?? null,
'og_site_name' => (string) config('seo.site_name', 'Skinbase'),
'twitter_card' => $twitterCard,
'twitter_title' => $this->twitter['title'] ?? $ogTitle,
@@ -325,4 +331,45 @@ final class SeoDataBuilder
return url($url);
}
private function absolutePageUrl(?string $url): ?string
{
$url = $this->clean($url);
if ($url === null) {
return null;
}
$base = rtrim((string) config('app.url', url('/')), '/');
if (preg_match('/^https?:\/\//i', $url) === 1) {
$parts = parse_url($url);
if ($parts === false) {
return $base;
}
$path = $parts['path'] ?? '/';
$query = isset($parts['query']) ? '?' . $parts['query'] : '';
$fragment = isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
return $base . ($path !== '' ? $path : '/') . $query . $fragment;
}
if ($url[0] === '?') {
return $this->defaultCanonicalUrl() . $url;
}
if ($url[0] !== '/') {
$url = '/' . $url;
}
return $base . $url;
}
private function defaultCanonicalUrl(): string
{
$request = app()->bound('request') ? app('request') : null;
$path = $request?->getPathInfo() ?: '/';
return $this->absolutePageUrl($path) ?? rtrim((string) config('app.url', url('/')), '/');
}
}