Optimierung 4.1: Web-Portal-/Domain-Kontext extrahiert
- WebPortalContext::fromRequest() buendelt die zuvor ~6x je Route-Closure wiederholte Host->Portal-Ableitung (portal, language, portalValues, domainConfig). - DomainAssetContext::applyWebDomainConfig() ist die Single Source of Truth fuer die Domain-/Theme-Anwendung (config + View::share + Vite); die fruehere $applyWebDomainConfig-Closure in routes/web.php ist nur noch ein duenner Delegations-Wrapper, damit Bare-Root- und Edition-Home-Route unveraendert per Closure aufrufen koennen. - Die 6 Portal-Seiten-Closures (Kategorie, Detail, Veroeffentlichen, Suche, Newsrooms, Newsroom-Detail) sowie Preise nutzen jetzt den zentralen Kontext. Abgesichert durch die 92 Web/-Tests (jede Seite laeuft ueber den neuen Kontext). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
987a5a2c42
commit
e5a63bcedd
4 changed files with 106 additions and 98 deletions
|
|
@ -3,10 +3,42 @@
|
|||
namespace App\Support;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
|
||||
class DomainAssetContext
|
||||
{
|
||||
/**
|
||||
* Wendet die Domain-/Theme-Konfiguration einer öffentlichen Web-Ausgabe an
|
||||
* (config + View::share + Vite) und liefert die aufgelöste Domain-Config.
|
||||
* Single Source of Truth für die zuvor je Route-Closure wiederholte Logik.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function applyWebDomainConfig(string $domainKey): array
|
||||
{
|
||||
/** @var array<string, mixed> $domainConfig */
|
||||
$domainConfig = config("domains.domains.{$domainKey}", []);
|
||||
|
||||
config([
|
||||
'app.theme' => $domainConfig['theme'] ?? $domainKey,
|
||||
'app.view_prefix' => $domainConfig['view_prefix'] ?? 'web',
|
||||
'app.domain_name' => $domainConfig['domain_name'] ?? request()->getHost(),
|
||||
'app.url' => $domainConfig['url'] ?? config('app.url'),
|
||||
]);
|
||||
|
||||
View::share('theme', $domainConfig['theme'] ?? $domainKey);
|
||||
View::share('viewPrefix', $domainConfig['view_prefix'] ?? 'web');
|
||||
View::share('domainName', $domainConfig['domain_name'] ?? request()->getHost());
|
||||
View::share('domainConfig', $domainConfig);
|
||||
View::share('domainUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
View::share('assetUrl', self::staticAssetOrigin($domainConfig));
|
||||
|
||||
self::configureVite($domainConfig);
|
||||
|
||||
return $domainConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $defaults
|
||||
* @param array<string, array<string, mixed>> $configuredDomains
|
||||
|
|
|
|||
42
app/Support/WebPortalContext.php
Normal file
42
app/Support/WebPortalContext.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Enums\Portal;
|
||||
|
||||
/**
|
||||
* Öffentlicher Portal-Kontext (Presseecho/Businessportal24) einer Web-Landingpage,
|
||||
* abgeleitet aus dem Host. Bündelt die zuvor in jeder Route-Closure wiederholte
|
||||
* Host→Portal-Ableitung samt Anwendung der Domain-/Theme-Konfiguration.
|
||||
*/
|
||||
class WebPortalContext
|
||||
{
|
||||
/**
|
||||
* @param list<string> $portalValues Eigenes Portal plus `both`.
|
||||
* @param array<string, mixed> $domainConfig
|
||||
*/
|
||||
private function __construct(
|
||||
public readonly string $domainKey,
|
||||
public readonly Portal $portal,
|
||||
public readonly string $language,
|
||||
public readonly array $portalValues,
|
||||
public readonly array $domainConfig,
|
||||
) {}
|
||||
|
||||
public static function fromRequest(): self
|
||||
{
|
||||
$host = request()->getHost();
|
||||
|
||||
[$domainKey, $portal] = str_contains($host, 'presseecho')
|
||||
? ['presseecho', Portal::Presseecho]
|
||||
: ['businessportal24', Portal::Businessportal24];
|
||||
|
||||
return new self(
|
||||
domainKey: $domainKey,
|
||||
portal: $portal,
|
||||
language: app()->getLocale(),
|
||||
portalValues: [$portal->value, Portal::Both->value],
|
||||
domainConfig: DomainAssetContext::applyWebDomainConfig($domainKey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ Keine Fachlogik-Änderung. Reduziert erreichbare Demo-Komponenten und Altlasten.
|
|||
|
||||
`routes/web.php` > 1100 Zeilen mit kompletter Datenaufbereitung. Schrittweise, gut absicherbar.
|
||||
|
||||
- [ ] **4.1 Portal-/Domain-Kontext extrahieren** (baut auf 3.3 `DomainContext` auf).
|
||||
- [x] **4.1 Portal-/Domain-Kontext extrahieren** — Die je Route-Closure ~6× wiederholte Host→Portal-Ableitung liegt jetzt in `WebPortalContext::fromRequest()` (liefert `portal`, `language`, `portalValues`, `domainConfig`). Die Domain-/Theme-Anwendung (config + `View::share` + Vite) ist als `DomainAssetContext::applyWebDomainConfig()` zentralisiert (vorher die `$applyWebDomainConfig`-Closure in `routes/web.php`, jetzt ein dünner Delegations-Wrapper, damit Bare-Root- und Edition-Home-Route unverändert bleiben). Abgesichert durch die 92 `Web/`-Tests, die jede Seite über den neuen Kontext laufen lassen.
|
||||
- [x] **4.2 Published-Release-Query zentralisieren** — vier komponierbare Scopes auf `PressRelease`: `scopePublished()` (Status + gesetztes Datum, ohne Zeitschranke → für Statistik-Fenster), `scopeVisibleInArchive()` (`published()` + `published_at <= now()`), `scopeForPortalValues(array)`, `scopeForLanguage(string)`. ~19 dupliziierte Query-Blöcke in `routes/web.php` (Home, Kategorie, Detail, Veröffentlichen, Suche, Newsrooms, Newsroom-Detail, Legacy-Redirect) darüber zentralisiert; jedes `withoutGlobalScope(PortalScope)` an seiner Stelle erhalten (Klausel-Reihenfolge ist für das Ergebnis irrelevant, da reine AND-Verknüpfung). Test: `Web/PublishedReleaseScopeTest`; die 89 bestehenden `Web/`-Tests bleiben grün.
|
||||
- [ ] **4.3 Page-Data in Services** — `HomePageData`/`CategoryPageData`/`NewsroomPageData` für Home/Kategorie/Newsroom/Detail/Suche/Preise.
|
||||
- [ ] **4.4 Routen auf schlanke (Invokable) Controller umstellen** — schrittweise je Seite.
|
||||
|
|
|
|||
128
routes/web.php
128
routes/web.php
|
|
@ -12,10 +12,10 @@ use App\Models\PressRelease;
|
|||
use App\Scopes\PortalScope;
|
||||
use App\Support\DomainAssetContext;
|
||||
use App\Support\EditorialClock;
|
||||
use App\Support\WebPortalContext;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
Route::get('/pm-vorschau/{token}', PressReleasePreviewController::class)
|
||||
|
|
@ -25,27 +25,9 @@ Route::get('/pm-vorschau/{token}', PressReleasePreviewController::class)
|
|||
// Gemeinsame Web-Routes für alle Landingpages
|
||||
// Jede Landing-Page hat das gleiche Gerüst, aber unterschiedliches Styling
|
||||
|
||||
$applyWebDomainConfig = static function (string $domainKey): array {
|
||||
$domainConfig = config("domains.domains.{$domainKey}", []);
|
||||
|
||||
config([
|
||||
'app.theme' => $domainConfig['theme'] ?? $domainKey,
|
||||
'app.view_prefix' => $domainConfig['view_prefix'] ?? 'web',
|
||||
'app.domain_name' => $domainConfig['domain_name'] ?? request()->getHost(),
|
||||
'app.url' => $domainConfig['url'] ?? config('app.url'),
|
||||
]);
|
||||
|
||||
View::share('theme', $domainConfig['theme'] ?? $domainKey);
|
||||
View::share('viewPrefix', $domainConfig['view_prefix'] ?? 'web');
|
||||
View::share('domainName', $domainConfig['domain_name'] ?? request()->getHost());
|
||||
View::share('domainConfig', $domainConfig);
|
||||
View::share('domainUrl', $domainConfig['url'] ?? config('app.url'));
|
||||
View::share('assetUrl', DomainAssetContext::staticAssetOrigin($domainConfig));
|
||||
|
||||
DomainAssetContext::configureVite($domainConfig);
|
||||
|
||||
return $domainConfig;
|
||||
};
|
||||
// Dünner Delegations-Wrapper auf die Single Source of Truth (DomainAssetContext),
|
||||
// damit Bare-Root- und Edition-Home-Route unverändert per Closure aufrufen können.
|
||||
$applyWebDomainConfig = static fn (string $domainKey): array => DomainAssetContext::applyWebDomainConfig($domainKey);
|
||||
|
||||
$webHomeData = static function (Portal $primaryPortal, string $language = 'de'): array {
|
||||
$portalValues = [$primaryPortal->value, Portal::Both->value];
|
||||
|
|
@ -269,18 +251,10 @@ $webHomeData = static function (Portal $primaryPortal, string $language = 'de'):
|
|||
};
|
||||
|
||||
// Kategorie-/Branchenseite je Ausgabe.
|
||||
$categoryPage = static function (string $edition, string $slug) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$categoryPage = static function (string $edition, string $slug) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
|
||||
$category = Category::query()
|
||||
->whereIn('portal', $portalValues)
|
||||
|
|
@ -475,18 +449,10 @@ $categoryPage = static function (string $edition, string $slug) use ($applyWebDo
|
|||
};
|
||||
|
||||
// Detailseite einer Pressemitteilung je Ausgabe.
|
||||
$releaseDetailPage = static function (string $edition, string $slug) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$releaseDetailPage = static function (string $edition, string $slug) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
|
||||
$publishedScope = static fn (Builder $query): Builder => $query
|
||||
->forPortalValues($portalValues)
|
||||
|
|
@ -553,18 +519,10 @@ $releaseDetailPage = static function (string $edition, string $slug) use ($apply
|
|||
};
|
||||
|
||||
// Veröffentlichen-Marketingseite je Ausgabe (Einreichung läuft im Publisher-Hub).
|
||||
$veroeffentlichenPage = static function (string $edition) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$veroeffentlichenPage = static function (string $edition) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
$referenceNow = EditorialClock::reference($portalValues, $language);
|
||||
$recentSince = $referenceNow->copy()->subDays(30);
|
||||
|
||||
|
|
@ -630,11 +588,11 @@ $veroeffentlichenPage = static function (string $edition) use ($applyWebDomainCo
|
|||
|
||||
// Preise-/Tarifseite je Ausgabe: Abos (Plan-Modell), Einzel-PM (config/billing)
|
||||
// und Add-on-Einzelkosten (config/credits). Inhalte liegen in lang/*/pricing.php.
|
||||
$preisePage = static function (string $edition) use ($applyWebDomainConfig) {
|
||||
$preisePage = static function (string $edition) {
|
||||
$domain = request()->getHost();
|
||||
|
||||
$domainKey = str_contains($domain, 'presseecho') ? 'presseecho' : 'businessportal24';
|
||||
$applyWebDomainConfig($domainKey);
|
||||
DomainAssetContext::applyWebDomainConfig($domainKey);
|
||||
|
||||
// Add-on-Einzelkosten aus der Credit-Ökonomie (1 Credit = 1 €).
|
||||
$extraPm = collect(config('credits.extra_pm', []))
|
||||
|
|
@ -670,18 +628,10 @@ $preisePage = static function (string $edition) use ($applyWebDomainConfig) {
|
|||
};
|
||||
|
||||
// Archiv-Suchseite je Ausgabe (interaktive Suche läuft in der Volt-Komponente).
|
||||
$suchePage = static function (string $edition) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$suchePage = static function (string $edition) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
|
||||
$mostReadReleases = PressRelease::query()
|
||||
->withoutGlobalScope(PortalScope::class)
|
||||
|
|
@ -694,25 +644,17 @@ $suchePage = static function (string $edition) use ($applyWebDomainConfig) {
|
|||
->get(['id', 'slug', 'title', 'hits', 'portal', 'language']);
|
||||
|
||||
return view('web.suche', [
|
||||
'portal' => $portal->value,
|
||||
'portal' => $context->portal->value,
|
||||
'language' => $language,
|
||||
'mostReadReleases' => $mostReadReleases,
|
||||
]);
|
||||
};
|
||||
|
||||
// Newsrooms-Verzeichnis je Ausgabe: alle aktiven Unternehmen mit Meldungen.
|
||||
$newsroomsIndex = static function (string $edition) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$newsroomsIndex = static function (string $edition) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
$referenceNow = EditorialClock::reference($portalValues, $language);
|
||||
$recentSince = $referenceNow->copy()->subDays(30);
|
||||
|
||||
|
|
@ -768,18 +710,10 @@ $newsroomsIndex = static function (string $edition) use ($applyWebDomainConfig)
|
|||
};
|
||||
|
||||
// Newsroom-Markenseite eines Unternehmens je Ausgabe: alle Meldungen der Firma.
|
||||
$newsroomDetail = static function (string $edition, string $slug) use ($applyWebDomainConfig) {
|
||||
$domain = request()->getHost();
|
||||
$language = app()->getLocale();
|
||||
|
||||
[$domainKey, $portal] = match (true) {
|
||||
str_contains($domain, 'presseecho') => ['presseecho', Portal::Presseecho],
|
||||
default => ['businessportal24', Portal::Businessportal24],
|
||||
};
|
||||
|
||||
$applyWebDomainConfig($domainKey);
|
||||
|
||||
$portalValues = [$portal->value, Portal::Both->value];
|
||||
$newsroomDetail = static function (string $edition, string $slug) {
|
||||
$context = WebPortalContext::fromRequest();
|
||||
$language = $context->language;
|
||||
$portalValues = $context->portalValues;
|
||||
|
||||
$company = Company::query()
|
||||
->withoutGlobalScope(PortalScope::class)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue