Arbeitsplan 14.09.2026, Punkte 1.1 bis 1.4 (Nachträge 6 bis 10 im Entwicklungsplan): - Farbwelt Interior (Tanne, Creme, Sand, Ocker, Clay) als Standard, Immobilien-Welt behält das bisherige Blau über body.world-immobilien; Logo je Welt, Favicons neu, Welten-Wechsler in Kopf- und Fußzeile. - Saubere Trennung der Welten: eigene Navigation, Ankündigungsleiste, Magazinliste, Über-uns-Seite, FAQ, Kontaktseite und Abschluss-CTA für Immobilien; Beiträge tragen eine Welt (Migration + Admin-Feld). Abschluss-CTA Ton in Ton statt Grün-zu-Ocker. - Kontakt: Betreffe in Gruppen Interior / Immobilien / B2in, Marken darin. Terminanfrage-Buttons öffnen das Widget mit vorgewähltem Anliegen; Select-Felder einheitlich gestaltet. - Tischlerei als vierte Kernmarke (Platzhalter), Kachelraster 2×2. - Herkunfts-Sektion „Aus der Region gefertigt“ mit schematischer Standortkarte und Ablauf CABINET (Beratung Bielefeld, Fertigung Köln, Aufbau mit Tischlerei). Deployment: migrate, WorldSplitCmsSeeder, CmsBrandSeeder, OriginSectionCmsSeeder (Abschnitt 8 im Entwicklungsplan). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
84 lines
2.3 KiB
PHP
84 lines
2.3 KiB
PHP
<?php
|
||
|
||
namespace App\Livewire\Web\Components\Sections;
|
||
|
||
use App\Models\CmsArticle;
|
||
use Livewire\Component;
|
||
|
||
class MagazinDetail extends Component
|
||
{
|
||
public $slug;
|
||
|
||
public $article;
|
||
|
||
public $relatedArticles;
|
||
|
||
public $content;
|
||
|
||
/** Rueckweg zur Magazinliste der Welt, zu der der Beitrag gehoert. */
|
||
public string $listUrl = '';
|
||
|
||
public function mount($slug)
|
||
{
|
||
$this->slug = (string) $slug;
|
||
$this->loadArticle();
|
||
$this->listUrl = $this->articleWorld() === CmsArticle::WORLD_IMMOBILIEN
|
||
? route('immobilien.magazin')
|
||
: route('magazin');
|
||
$this->loadRelatedArticles();
|
||
$this->loadThemeContent();
|
||
}
|
||
|
||
private function articleWorld(): string
|
||
{
|
||
return (string) ($this->article['world'] ?? CmsArticle::WORLD_INTERIOR);
|
||
}
|
||
|
||
private function loadArticle()
|
||
{
|
||
$articles = $this->getArticlesData();
|
||
|
||
// Unbekannte Slugs haben frueher stillschweigend Artikel 1 ausgeliefert – mit Status 200.
|
||
// Das erzeugt Duplicate Content unter beliebig vielen URLs.
|
||
abort_unless(isset($articles[$this->slug]), 404);
|
||
|
||
$this->article = $articles[$this->slug];
|
||
}
|
||
|
||
private function loadRelatedArticles()
|
||
{
|
||
$this->relatedArticles = collect($this->getArticlesData())
|
||
->filter(fn (array $article) => ($article['world'] ?? CmsArticle::WORLD_INTERIOR) === $this->articleWorld())
|
||
->reject(fn (array $article, string $slug) => $slug === $this->slug)
|
||
->take(2)
|
||
->values()
|
||
->toArray();
|
||
}
|
||
|
||
private function loadThemeContent()
|
||
{
|
||
$this->content = cms_theme_section('magazin_detail');
|
||
}
|
||
|
||
/** @var array<string, array<string, mixed>>|null */
|
||
private ?array $articlesCache = null;
|
||
|
||
/**
|
||
* Wird beim Rendern zweimal gebraucht (Artikel + verwandte Artikel) – deshalb gemerkt,
|
||
* damit die Detailseite nur eine Abfrage auslöst.
|
||
*
|
||
* @return array<string, array<string, mixed>>
|
||
*/
|
||
private function getArticlesData(): array
|
||
{
|
||
return $this->articlesCache ??= CmsArticle::forFrontend();
|
||
}
|
||
|
||
public function render()
|
||
{
|
||
return view('livewire.web.components.sections.magazin-detail', [
|
||
'article' => $this->article,
|
||
'relatedArticles' => $this->relatedArticles,
|
||
]);
|
||
}
|
||
}
|