*/ public const LEGACY_ID_TO_SLUG = [ 1 => 'escrow-system-dubai-investoren', 2 => 'spotlight-al-jaddaf-hotspot', 3 => 'turnkey-investments-moeblierung-rendite', 4 => 'supply-chain-management-vertragssicherheit', 5 => 'local-for-local-regionaler-moebelhandel', ]; public const WORLD_INTERIOR = 'interior'; public const WORLD_IMMOBILIEN = 'immobilien'; /** @var array */ public const WORLDS = [self::WORLD_INTERIOR, self::WORLD_IMMOBILIEN]; /** * Beiträge aus dem Bestand, die zur Immobilien-Welt gehören (Dubai-Themen). * * @var array */ public const LEGACY_IMMOBILIEN_SLUGS = [ 'escrow-system-dubai-investoren', 'spotlight-al-jaddaf-hotspot', 'turnkey-investments-moeblierung-rendite', ]; protected $fillable = [ 'slug', 'title', 'subtitle', 'image', 'category', 'world', 'date_label', 'read_time', 'author', 'content', 'is_published', 'order', ]; /** @var array */ public array $translatable = [ 'title', 'subtitle', 'content', ]; protected function casts(): array { return [ 'author' => 'array', 'is_published' => 'boolean', 'order' => 'integer', ]; } public function scopePublished(Builder $query): Builder { return $query->where('is_published', true); } public function scopeOrdered(Builder $query): Builder { return $query->orderBy('order')->orderByDesc('created_at'); } public function scopeForWorld(Builder $query, string $world): Builder { return $query->where('world', $world); } /** * Veröffentlichte Artikel fürs Frontend, nach Slug indiziert. * * Das CMS gewinnt. Solange keine Artikel gepflegt sind, greift die Lang-Datei als Fallback – * dieselbe Reihenfolge wie bei cms_theme_section(), damit das Magazin auf einer frischen * Installation nicht leer ist. * * Mit `$world` nur die Beiträge dieser Farbwelt (Interior oder Immobilien) – die beiden * Magazinlisten sind getrennt, die Detailseite rendert in der Welt ihres Beitrags. * * @return array> */ public static function forFrontend(?string $world = null): array { $maintained = static::query()->published()->exists(); if ($maintained) { return static::query() ->published() ->when($world !== null, fn (Builder $query) => $query->forWorld($world)) ->ordered() ->get() ->mapWithKeys(fn (self $article) => [$article->slug => $article->toFrontendArray()]) ->all(); } $articles = static::fallbackFromLang(); if ($world === null) { return $articles; } return array_filter($articles, fn (array $article) => ($article['world'] ?? self::WORLD_INTERIOR) === $world); } /** * Slug zu einer alten numerischen Magazin-ID, falls bekannt. */ public static function slugForLegacyId(int|string $id): ?string { return self::LEGACY_ID_TO_SLUG[(int) $id] ?? null; } /** * Slug eines Artikels aus der Lang-Datei. * * Neue Artikel bringen ihren Slug selbst mit (`'slug' => …`). Die fünf ursprünglichen * Artikel hatten keinen und werden weiter über {@see LEGACY_ID_TO_SLUG} aufgelöst – die * Konstante bleibt damit das, was sie ist: die Karte für die alten `/magazin/{id}`-URLs. * * @param array $article */ public static function slugForLangArticle(int|string $id, array $article): ?string { $slug = $article['slug'] ?? null; if (is_string($slug) && $slug !== '') { return $slug; } return self::slugForLegacyId($id); } /** * @return array> */ private static function fallbackFromLang(): array { $articles = trans('b2in.articles'); if (! is_array($articles)) { return []; } $out = []; foreach ($articles as $id => $article) { if (! is_array($article) || ($article['published'] ?? true) === false) { continue; } $slug = self::slugForLangArticle($id, $article); if ($slug === null) { continue; } $out[$slug] = $article + ['slug' => $slug, 'world' => self::WORLD_INTERIOR]; } return $out; } /** * @return array */ public function toFrontendArray(): array { return [ 'id' => $this->id, 'slug' => $this->slug, 'title' => $this->title, 'subtitle' => $this->subtitle, 'image' => $this->image, 'category' => $this->category, 'world' => $this->world ?? self::WORLD_INTERIOR, 'date' => $this->date_label, 'readTime' => $this->read_time, 'author' => $this->author ?? [], 'content' => $this->content ?? [], ]; } }