web: E9 und E10 entschieden — Zuordnung aktiv, eine Adresse je statischer Seite, Hub-Portalseiten leiten weiter
E9: Kategorie-Zuordnung wie vorgeschlagen übernommen, Zuordnung ist standardmäßig an (CANONICAL_ASSIGNMENT_ENABLED). Auf der Dev-Kopie verifiziert: Kopie trägt rel=canonical auf die Erstquelle und fällt aus der Sitemap ihres Portals. E10: faq, hilfe, gute-pressemitteilung, die vier Transparenzseiten und die API-Doku lagen auf allen drei Hosts mit identischem Text und je eigenem Self-Canonical. Jetzt führt presseecho die Informationsseiten, der Hub die API-Doku; Sitemap und llms.txt nennen nur die führende Adresse. Rechtstexte (Impressum, Datenschutz, AGB, Richtlinien, Meldeweg, Kündigung) bleiben bewusst je Host — jede Domain ist ein eigenes Angebot mit eigener Anbieterkennzeichnung. Bei der Erreichbarkeitsprüfung des Hubs gefunden und behoben: /de/preise, /de/veroeffentlichen, /de/newsrooms und /de/suche wurden unter pressekonto.com mit dem Branding von businessportal24 ausgeliefert (der Portal-Kontext fällt dort darauf zurück) — jetzt 301 auf presseecho, die Transparenz-Navigation verlinkt direkt dorthin. /de/kontakt und /de/ueber-uns sprangen über /de auf die Startseite, jetzt in einem Schritt. Der Self-Canonical kommt jetzt aus dem aufgerufenen Host; Portal-Inhalte, die über den Hub laufen, behalten ihr Portal (webPortalContextApplied). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ee267c622e
commit
4c17a17254
18 changed files with 434 additions and 17 deletions
116
app/Support/StaticPageCanonical.php
Normal file
116
app/Support/StaticPageCanonical.php
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
$url = config('domains.domains.'.config('canonical.portal_only_host', 'presseecho').'.url');
|
||||
|
||||
return is_string($url) && $url !== '' ? rtrim($url, '/') : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Support;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
/**
|
||||
* Öffentlicher Portal-Kontext (Presseecho/Businessportal24) einer Web-Landingpage,
|
||||
|
|
@ -39,6 +40,12 @@ class WebPortalContext
|
|||
$portal = self::portalFromHost();
|
||||
$domainKey = $portal === Portal::Presseecho ? 'presseecho' : 'businessportal24';
|
||||
|
||||
// Merker fürs Layout: Diese Seite rendert Portal-Inhalte, die
|
||||
// Domain-Config zeigt also auf ein Portal — auch wenn der Aufruf über
|
||||
// den Hub kam. Der Self-Canonical muss dann dem Portal folgen, nicht
|
||||
// dem aufgerufenen Host (Anhang 2, E10).
|
||||
View::share('webPortalContextApplied', true);
|
||||
|
||||
return new self(
|
||||
domainKey: $domainKey,
|
||||
portal: $portal,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue