presseportale/routes/web.php
Kevin Adametz 0efabaf446 Multi-Domain-Asset-Infrastruktur: geteilte Vite-Konfiguration und DomainAssetContext
- vite.shared.js als gemeinsame Quelle fuer Ports, Hot-Files, HMR-Hosts
  und CORS-Origins der beiden Vite-Builds (Portal/Web)
- App\Support\DomainAssetContext kapselt die Vite-Build-Directory-
  Konfiguration pro Domain (ThemeServiceProvider + Auth-Layout nutzen ihn)
- Tailwind-Portal-Content-Globs auf die tatsaechliche View-Struktur gezogen
- Dev-Beispiel-Routen + Tests (DomainAssetContextTest, DevExampleRoutesTest)
- Aufraeumen: versehentliche Leerdatei dev:web entfernt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:16:09 +00:00

323 lines
11 KiB
PHP

<?php
use App\Enums\Portal;
use App\Enums\PressReleaseStatus;
use App\Http\Controllers\PressReleasePreviewController;
use App\Models\Category;
use App\Models\Company;
use App\Models\PressRelease;
use App\Support\DomainAssetContext;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
Route::get('/pm-vorschau/{token}', PressReleasePreviewController::class)
->where('token', '[A-Za-z0-9]{40,}')
->name('press-releases.preview');
// 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;
};
$webHomeData = static function (Portal $primaryPortal): array {
$portalValues = [$primaryPortal->value, Portal::Both->value];
$publishedQuery = static fn (): Builder => PressRelease::query()
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->whereNotNull('published_at')
->where('published_at', '<=', now());
$with = [
'company',
'category.translations' => fn ($query) => $query->where('locale', 'de'),
'images' => fn ($query) => $query
->orderByDesc('is_preview')
->orderBy('sort_order')
->limit(1),
];
$oldestPublishedAt = $publishedQuery()
->oldest('published_at')
->value('published_at');
$leadRelease = $publishedQuery()
->with($with)
->orderByDesc('published_at')
->first();
$sideReleases = $publishedQuery()
->with($with)
->when($leadRelease, fn (Builder $query) => $query->where('id', '!=', $leadRelease->id))
->orderByDesc('published_at')
->limit(4)
->get();
$mostReadReleases = $publishedQuery()
->orderByDesc('hits')
->orderByDesc('published_at')
->limit(4)
->get(['id', 'slug', 'title', 'hits', 'portal', 'language']);
$activeNewsrooms = Company::query()
->whereIn('portal', $portalValues)
->where('is_active', true)
->whereHas('pressReleases', function (Builder $query) use ($portalValues): void {
$query
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->whereNotNull('published_at')
->where('published_at', '>=', now()->subDays(7));
})
->withCount([
'pressReleases as recent_releases_count' => function (Builder $query) use ($portalValues): void {
$query
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->whereNotNull('published_at')
->where('published_at', '>=', now()->subDays(7));
},
'pressReleases as today_releases_count' => function (Builder $query) use ($portalValues): void {
$query
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->whereDate('published_at', today());
},
])
->orderByDesc('today_releases_count')
->orderByDesc('recent_releases_count')
->limit(6)
->get()
->map(fn (Company $company): array => [
'name' => $company->name,
'slug' => $company->slug,
'initial' => mb_strtoupper(mb_substr((string) $company->name, 0, 1)),
'count' => (int) $company->recent_releases_count,
'today' => (int) $company->today_releases_count > 0,
]);
$industryIndex = Category::query()
->with(['translations' => fn ($query) => $query->where('locale', 'de')])
->withCount([
'pressReleases as recent_count' => function (Builder $query) use ($portalValues): void {
$query
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->where('published_at', '>=', now()->subDays(7))
->whereNotNull('published_at');
},
'pressReleases as previous_count' => function (Builder $query) use ($portalValues): void {
$query
->whereIn('portal', $portalValues)
->where('status', PressReleaseStatus::Published)
->where('language', 'de')
->where('published_at', '>=', now()->subDays(14))
->where('published_at', '<', now()->subDays(7));
},
])
->whereIn('portal', $portalValues)
->where('is_active', true)
->whereNull('parent_id')
->orderByDesc('recent_count')
->limit(7)
->get()
->map(function (Category $category): ?array {
$translation = $category->translations->first();
if (! $translation) {
return null;
}
return [
'name' => $translation->name,
'href' => $translation->slug ? route('kategorie', ['slug' => $translation->slug]) : route('kategorien'),
'count' => (int) ($category->recent_count ?? 0),
'delta' => (int) ($category->recent_count ?? 0) - (int) ($category->previous_count ?? 0),
];
})
->filter()
->values();
return [
'leadRelease' => $leadRelease,
'sideReleases' => $sideReleases,
'mostReadReleases' => $mostReadReleases,
'activeNewsrooms' => $activeNewsrooms,
'industryIndex' => $industryIndex,
'homeStats' => [
'publishedCount' => $publishedQuery()->count(),
'publishedToday' => $publishedQuery()->whereDate('published_at', today())->count(),
'archiveSince' => $oldestPublishedAt ? (int) Carbon::parse($oldestPublishedAt)->format('Y') : null,
],
];
};
// Hauptseite - dynamisch basierend auf Domain
Route::get('/', function () use ($applyWebDomainConfig, $webHomeData) {
$domain = request()->getHost();
if (str_contains($domain, 'presseecho')) {
$applyWebDomainConfig('presseecho');
return view('web.presseecho', $webHomeData(Portal::Presseecho));
} elseif (str_contains($domain, 'businessportal24')) {
$applyWebDomainConfig('businessportal24');
return view('web.businessportal24', $webHomeData(Portal::Businessportal24));
} elseif (str_contains($domain, 'pressekonto')) {
$applyWebDomainConfig('pressekonto');
return view('web.pressekonto');
}
$applyWebDomainConfig('businessportal24');
return view('web.businessportal24', $webHomeData(Portal::Businessportal24));
})->name('home');
Route::get('/variant-1', function () {
$domain = request()->getHost();
// Domain-basierte View-Auswahl
if (str_contains($domain, 'presseecho')) {
return view('web.presseecho');
} elseif (str_contains($domain, 'businessportal24')) {
return view('web.businessportal24-variant-float-glow');
}
return view('web.businessportal24-variant-float-glow');
})->name('variant-1');
Route::get('/variant-2', function () {
$domain = request()->getHost();
// Domain-basierte View-Auswahl
if (str_contains($domain, 'presseecho')) {
return view('web.presseecho');
} elseif (str_contains($domain, 'businessportal24')) {
return view('web.businessportal24-variant-glass-gradient');
}
});
// Preise & Leistungen
Route::get('/preise', function () {
return view('web.preise');
})->name('preise');
// FAQ - Häufig gestellte Fragen
Route::get('/faq', function () {
return view('web.faq');
})->name('faq');
// Kontakt
Route::get('/kontakt', function () {
return view('web.kontakt');
})->name('kontakt');
// Über uns
Route::get('/ueber-uns', function () {
return view('web.ueber-uns');
})->name('ueber-uns');
// Veröffentlichen
Route::get('/veroeffentlichen', function () {
return view('web.veroeffentlichen');
})->name('veroeffentlichen');
// Kategorien
Route::get('/kategorien', function () {
return view('web.kategorien');
})->name('kategorien');
// Suche
Route::get('/suche', function () {
return view('web.suche');
})->name('suche');
// Services
Route::get('/newsrooms', function () {
return view('web.newsrooms');
})->name('newsrooms');
Route::get('/api', function () {
return view('web.api');
})->name('api');
Route::get('/docs/api/v1', function () {
return response((string) file_get_contents(base_path('docs/api/v1.yml')), 200, [
'Content-Type' => 'application/yaml; charset=UTF-8',
]);
})->name('docs.api.v1');
// Über uns - Unterseiten
Route::get('/team', function () {
return view('web.team');
})->name('team');
Route::get('/partner', function () {
return view('web.partner');
})->name('partner');
Route::get('/karriere', function () {
return view('web.karriere');
})->name('karriere');
Route::get('/presse', function () {
return view('web.presse');
})->name('presse');
// Hilfe & Support
Route::get('/hilfe', function () {
return view('web.hilfe');
})->name('hilfe');
// Rechtliches
Route::get('/impressum', function () {
return view('web.impressum');
})->name('impressum');
Route::get('/datenschutz', function () {
return view('web.datenschutz');
})->name('datenschutz');
Route::get('/agb', function () {
return view('web.agb');
})->name('agb');
Route::get('/cookies', function () {
return view('web.cookies');
})->name('cookies');
// Kategorie Seite (z.B. IT & Software)
Route::get('/kategorie/{slug}', function ($slug) {
return view('web.kategorie', ['categorySlug' => $slug]);
})->name('kategorie');
// Release Detail Seite
Route::get('/release/{slug}', function ($slug) {
return view('web.release-detail', ['releaseSlug' => $slug]);
})->name('release.detail');