presseportale/app/Support/StaticPageCanonical.php
Kevin Adametz edb2d49199 web: Portal-Inhalte sind auf dem Hub nicht mehr erreichbar (Anhang 2, E10)
Kategorien, Jahresarchive, Meldungen und Newsrooms liefen auch unter
pressekonto.com — die Ausgabe-Routen gelten für alle Hosts, und der
Portal-Kontext fällt dort auf businessportal24 zurück. Jetzt 301 auf das
Portal.

Meldung und Newsroom folgen ihrem eigenen Portal, nicht einer festen
Vorgabe; bei Doppelveröffentlichung (gleicher Slug auf beiden Portalen)
gewinnt die Erstquelle, sonst endete die Weiterleitung auf der Kopie.
Kategorie, Archiv und Suche gibt es auf beiden Portalen — dort entscheidet
canonical.portal_only_host.

Bewusst nicht weitergeleitet wird ein unbekannter Slug (sonst 301 in ein
404) und alles, was kein GET ist (ein 301 auf POST verliert die Anfrage —
Newsroom beanspruchen und Share-Zählung laufen weiter). Query-Parameter
bleiben erhalten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 09:15:04 +00:00

142 lines
4.7 KiB
PHP

<?php
namespace App\Support;
use App\Enums\Portal;
/**
* Kanonischer Host statischer Seiten (Roadmap Anhang 2, E10).
*
* faq, hilfe, Transparenzseiten und die API-Doku werden auf Hub und beiden
* Portalen mit identischem Text ausgeliefert. Damit nicht drei Adressen um
* denselben Text konkurrieren, führt je Seite ein Host; die beiden anderen
* verweisen per rel=canonical dorthin. Rechtstexte sind bewusst
* ausgenommen — sie gehören zu ihrer Domain (siehe config/canonical.php).
*/
class StaticPageCanonical
{
/**
* Kanonische Adresse der aktuellen Seite oder null, wenn die Seite
* self-canonical bleibt.
*/
public static function urlFor(?string $routeName, string $path): ?string
{
$base = self::baseFor($routeName);
return $base === null ? null : $base.$path;
}
/**
* Basis-URL des führenden Hosts einer Route, oder null.
*/
public static function baseFor(?string $routeName): ?string
{
if ($routeName === null) {
return null;
}
$domainKey = config('canonical.static_page_host.'.$routeName);
if (! is_string($domainKey)) {
return null;
}
$url = config('domains.domains.'.$domainKey.'.url');
return is_string($url) && $url !== '' ? rtrim($url, '/') : null;
}
/**
* Basis der Selbst-Referenz: die konfigurierte URL des Hosts, unter dem
* die Seite tatsächlich aufgerufen wurde.
*
* Der geteilte `$domainUrl` aus dem ThemeServiceProvider taugt dafür
* nicht immer — er wird beim Booten aus dem damaligen Host abgeleitet
* und von `WebPortalContext::fromRequest()` auf ein Portal umgestellt,
* auf dem Hub also auf businessportal24. Maßgeblich ist der Host der
* Anfrage — außer bei Portal-Inhalten, die über den Hub aufgerufen
* wurden ($portalContextApplied).
*/
public static function selfBase(?string $domainUrl = null, bool $portalContextApplied = false): string
{
$host = request()->getHost();
// Portal-Inhalte (Kategorie, Archiv, Meldung, Newsroom) sind auch
// über den Hub erreichbar und rendern dort das Portal. Ihr Canonical
// gehört auf das Portal, nicht auf pressekonto.
if ($portalContextApplied && is_string($domainUrl) && $domainUrl !== '') {
return rtrim($domainUrl, '/');
}
if (is_string($domainUrl) && $domainUrl !== '' && parse_url($domainUrl, PHP_URL_HOST) === $host) {
return rtrim($domainUrl, '/');
}
foreach ((array) config('domains.domains', []) as $domain) {
$url = $domain['url'] ?? null;
if (is_string($url) && parse_url($url, PHP_URL_HOST) === $host) {
return rtrim($url, '/');
}
}
return rtrim(request()->getSchemeAndHttpHost(), '/');
}
/**
* Adresse für einen Link auf eine benannte Seite: normalerweise die des
* aufgerufenen Hosts — bei Portalseiten auf dem Hub direkt die des
* führenden Portals, damit der Besucher nicht über eine Weiterleitung
* läuft.
*/
public static function publicUrlFor(string $routeName): string
{
$url = route($routeName);
$portalBase = self::portalOnlyRedirect($routeName);
if ($portalBase !== null && str_contains(request()->getHost(), 'pressekonto')) {
return $portalBase.(string) parse_url($url, PHP_URL_PATH);
}
return $url;
}
/**
* Seiten, die es nur auf den Portalen gibt — auf dem Hub führen sie auf
* das führende Portal statt auf eine Fassung mit fremdem Branding.
*/
public static function portalOnlyRedirect(?string $routeName): ?string
{
if ($routeName === null || ! in_array($routeName, (array) config('canonical.portal_only_pages', []), true)) {
return null;
}
return self::defaultPortalBase();
}
/**
* Basis-URL des Standard-Portals (config canonical.portal_only_host).
*/
public static function defaultPortalBase(): ?string
{
$url = config('domains.domains.'.config('canonical.portal_only_host', 'presseecho').'.url');
return is_string($url) && $url !== '' ? rtrim($url, '/') : null;
}
/**
* Basis-URL eines Portals aus dem Enum-Wert (`presseecho`,
* `businessportal24`); `both` und Unbekanntes fallen auf das
* Standard-Portal zurück.
*/
public static function portalBaseFor(?string $portal): ?string
{
if ($portal === null || $portal === Portal::Both->value) {
return self::defaultPortalBase();
}
$url = config('domains.domains.'.$portal.'.url');
return is_string($url) && $url !== '' ? rtrim($url, '/') : self::defaultPortalBase();
}
}