b2in/packages/flux-cms/SETUP.md
2026-04-10 17:18:17 +02:00

14 KiB

Flux CMS — Schritt-für-Schritt Setup-Anleitung

Diese Anleitung beschreibt die Integration von Flux CMS in ein neues Laravel-Projekt.

Migrierst du ein bestehendes Projekt? Dann nutze stattdessen die kompakte MIGRATION.md Checkliste.

Voraussetzungen

  • Laravel 11+ oder 12
  • Livewire 4 mit Volt
  • Flux UI (Free oder Pro)
  • spatie/laravel-translatable (wird vom Package mitgebracht)
  • intervention/image v3 (für Bildoptimierung)
  • Tailwind CSS v4
  • Heroicons (via blade-ui-kit/blade-heroicons)
  • Mehrsprachige lang/-Dateien (DE/EN oder andere)

1. Package installieren

1.1 Repository registrieren

// composer.json
{
    "repositories": [
        { "type": "path", "url": "package/flux-cms/core" }
    ]
}

1.2 Dependencies hinzufügen

composer require flux-cms/core:@dev
composer require intervention/image

1.3 Konfiguration publizieren

php artisan vendor:publish --tag=flux-cms-config

1.4 Migrations ausführen

php artisan migrate

Dies erstellt folgende Tabellen:

  • flux_cms_contents — Alle Seiteninhalte (Key-Value mit Übersetzungen)
  • flux_cms_news_items — Nachrichteneinträge
  • flux_cms_industries — Branchen-Band
  • flux_cms_faqs — FAQ-Einträge
  • flux_cms_downloads — Downloads (PDFs, etc.)
  • flux_cms_linkedin_posts — LinkedIn-Posts
  • flux_cms_media — Medienbibliothek (Bilder, PDFs)

Hinweis: Je nach Projekt-Erweiterung können zusätzliche Migrations nötig sein (z.B. für erweiterte Downloads mit Highlights/Checkpoints).


2. Helper-Funktionen einrichten

2.1 Datei erstellen

Erstelle app/helpers.php:

<?php

use FluxCms\Core\Models\CmsMedia;
use FluxCms\Core\Services\CmsContentService;

if (! function_exists('cms')) {
    function cms(string $key, array $replace = [], ?string $locale = null): mixed
    {
        return app(CmsContentService::class)->get($key, $replace, $locale);
    }
}

if (! function_exists('tcms')) {
    function tcms(string $key, array $replace = [], ?string $locale = null): string
    {
        $text = cms($key, $replace, $locale);
        return is_string($text) ? $text : (string) $text;
    }
}

if (! function_exists('cms_media_url')) {
    /**
     * Resolve a CmsContent key (type=image) to a full media URL.
     * Falls back to asset('assets/images/...') if not found in media library.
     */
    function cms_media_url(string $key, string $profile = ''): string
    {
        $filename = cms($key);
        if (! $filename || ! is_string($filename) || $filename === $key) {
            $fallback = str_replace('.', '/', $key);
            return asset('assets/images/' . basename($fallback));
        }
        return media_url($filename, $profile);
    }
}

if (! function_exists('media_url')) {
    /**
     * Resolve a CmsMedia filename to its full storage URL.
     * Uses in-memory cache to avoid repeated DB queries.
     */
    function media_url(?string $filename, string $profile = ''): string
    {
        if (! $filename || $filename === '') {
            return '';
        }

        static $resolved = [];
        $cacheKey = $filename . '|' . $profile;

        if (isset($resolved[$cacheKey])) {
            return $resolved[$cacheKey];
        }

        $media = CmsMedia::where('filename', $filename)->first();
        if (! $media) {
            $resolved[$cacheKey] = asset('assets/images/' . $filename);
            return $resolved[$cacheKey];
        }

        if ($profile && $media->hasConversion($profile)) {
            $resolved[$cacheKey] = $media->getConversionUrl($profile);
        } else {
            $resolved[$cacheKey] = $media->getUrl();
        }

        return $resolved[$cacheKey];
    }
}

2.2 Autoload registrieren

// composer.json → autoload
{
    "autoload": {
        "files": ["app/helpers.php"]
    }
}
composer dump-autoload

3. Admin-Oberfläche einrichten

3.1 Layout erstellen

Kopiere core/resources/views/admin-reference/layout-cms.blade.php nach resources/views/components/layouts/cms.blade.php.

Passe die Sidebar-Navigation an:

  • Branding/Logo
  • Navigationspunkte: Dashboard, Inhalte, Medienbibliothek, News, Industries, Downloads, Team, FAQs, LinkedIn
  • Benutzer-Menü

Wichtig: Das Layout muss <flux:toast /> enthalten für die Benachrichtigungen.

3.2 Admin Views kopieren

mkdir -p resources/views/livewire/admin/cms/

cp package/flux-cms/core/resources/views/admin-reference/cms/*.blade.php \
   resources/views/livewire/admin/cms/
View Datei Beschreibung
Dashboard dashboard-index.blade.php Übersicht mit Statistiken
Inhalte content-index.blade.php Haupteditor (Text/HTML/Image/JSON)
Medienbibliothek media-index.blade.php Zentrale Medienverwaltung (Grid+Liste)
News news-index.blade.php CRUD mit MediaPicker für Bild + PDF
Industries industries-index.blade.php CRUD mit Sortierung
FAQs faqs-index.blade.php CRUD nach Kategorien
LinkedIn linkedin-index.blade.php CRUD mit MediaPicker
Downloads downloads-index.blade.php CRUD für Case Studies/Capabilities/Stories
Team team-index.blade.php CRUD mit MediaPicker für Profilbilder
Suchindex search-index.blade.php Seitensuche: Keywords, Kategorien, Vorschau

3.3 Livewire-Komponenten einrichten

Funktionale Volt-Komponenten können kein WithFileUploads verwenden. Daher gibt es class-based Livewire-Komponenten:

# Multi-File Upload
cp package/flux-cms/core/src/Helpers/MediaLibraryUploader.php \
   app/Livewire/Admin/Cms/MediaLibraryUploader.php

# Medienauswahl-Modal
cp package/flux-cms/core/src/Helpers/MediaPicker.php \
   app/Livewire/Admin/Cms/MediaPicker.php

Namespace in beiden Dateien anpassen:

namespace App\Livewire\Admin\Cms;

3.4 Blade-Views für Livewire-Komponenten

# MediaLibraryUploader View
cp package/flux-cms/core/resources/views/admin-reference/cms/media-library-uploader.blade.php \
   resources/views/livewire/admin/cms/media-library-uploader.blade.php

# MediaPicker View
cp package/flux-cms/core/resources/views/admin-reference/cms/media-picker.blade.php \
   resources/views/livewire/admin/cms/media-picker.blade.php

3.5 Routes registrieren

// routes/web.php
use Livewire\Volt\Volt;

Route::middleware(['auth'])->group(function () {
    Volt::route('admin/cms', 'admin.cms.dashboard-index')->name('cms.dashboard');
    Volt::route('admin/cms/content', 'admin.cms.content-index')->name('cms.content.index');
    Volt::route('admin/cms/media', 'admin.cms.media-index')->name('cms.media.index');
    Volt::route('admin/cms/news', 'admin.cms.news-index')->name('cms.news.index');
    Volt::route('admin/cms/industries', 'admin.cms.industries-index')->name('cms.industries.index');
    Volt::route('admin/cms/faqs', 'admin.cms.faqs-index')->name('cms.faqs.index');
    Volt::route('admin/cms/linkedin', 'admin.cms.linkedin-index')->name('cms.linkedin.index');
    Volt::route('admin/cms/downloads', 'admin.cms.downloads-index')->name('cms.downloads.index');
    Volt::route('admin/cms/team', 'admin.cms.team-index')->name('cms.team.index');
    Volt::route('admin/cms/search-index', 'admin.cms.search-index')->name('cms.search-index');
});

4. Medienbibliothek einrichten

php artisan storage:link

4.2 Storage-Verzeichnisse

Die Verzeichnisse werden automatisch erstellt beim ersten Upload:

  • storage/app/public/cms/media/originals/ — Original-Uploads
  • storage/app/public/cms/media/conversions/ — Generierte Bildgrößen
  • storage/app/public/cms/media/thumbnails/ — Auto-Thumbnails

4.3 Bildprofile konfigurieren

In config/flux-cms.php die Conversion-Profile an dein Projekt anpassen:

'media' => [
    'max_upload_size' => 20480,  // KB
    'allowed_types' => ['jpg', 'jpeg', 'png', 'webp', 'gif', 'svg', 'pdf', 'doc', 'docx'],
    'storage_disk' => 'public',
    'profiles' => [
        'hero'      => ['width' => 1920, 'height' => 800,  'format' => 'webp', 'quality' => 85],
        'card'      => ['width' => 768,  'height' => 512,  'format' => 'webp', 'quality' => 80],
        'thumbnail' => ['width' => 400,  'height' => 300,  'format' => 'webp', 'quality' => 75],
        'avatar'    => ['width' => 400,  'height' => 400,  'format' => 'webp', 'quality' => 80],
        'news'      => ['width' => 1200, 'height' => 630,  'format' => 'webp', 'quality' => 80],
        'thumb'     => ['width' => 200,  'height' => 200,  'format' => 'webp', 'quality' => 70],
    ],
],

4.4 HTTPS / Proxy

Falls hinter einem Reverse Proxy, in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(at: '*');
})

Und in AppServiceProvider::boot():

if (request()->header('X-Forwarded-Proto') === 'https' || app()->environment('production')) {
    URL::forceScheme('https');
}

5. Inhalte importieren (Seeder)

5.1 Seeders kopieren & anpassen

cp package/flux-cms/core/database/seeders-reference/*.php database/seeders/

5.2 CmsContentSeeder anpassen

protected array $skipFiles = [
    'faqs', 'sections', 'validation', 'auth', 'passwords', 'pagination',
];

protected array $skipKeys = [
    'components' => ['news_band', 'industries_band'],
];

5.3 CmsMediaSeeder erstellen

Erstelle einen Seeder, der alle hochgeladenen Medien und die CmsContent-Einträge vom Typ image wiederherstellt. Dieser wird nach dem CmsContentSeeder ausgeführt, um Image-Keys zu erzeugen (z.B. welcome.hero.image → Dateiname).

5.4 CmsDownloadSeeder erstellen (optional)

Falls du Downloads (Case Studies, Capabilities, Success Stories) verwendest, erstelle einen Seeder mit den vollständigen Inline-Daten.

5.5 DatabaseSeeder registrieren

public function run(): void
{
    $this->call([
        CmsContentSeeder::class,
        CmsMediaSeeder::class,
        CmsNewsItemSeeder::class,
        CmsIndustrySeeder::class,
        CmsFaqSeeder::class,
        CmsLinkedinPostSeeder::class,
        CmsDownloadSeeder::class,
    ]);
}

5.6 Seeding ausführen

php artisan db:seed

6. Frontend umstellen

6.1 __() durch cms() ersetzen

- {{ __('welcome.hero.heading') }}
+ {{ cms('welcome.hero.heading') }}

6.2 Bilder über Medienbibliothek laden

- <img src="{{ asset('assets/images/keyvisual.webp') }}" />
+ <img src="{{ cms_media_url('welcome.hero.image', 'hero') }}" />

Oder direkt über Dateiname:

<img src="{{ media_url($item['image'], 'card') }}" />

6.3 Downloads über CmsDownload

@foreach (CmsDownload::published()->byCategory('case_study')->ordered()->get() as $dl)
    <x-download-article-card :article="$dl->toFrontendArray()" :index="$loop->index" />
@endforeach

6.4 News über CmsNewsItem

@php
    $items = CmsNewsItem::published()->ordered()->get()
        ->map(fn($i) => $i->toFrontendArray())->toArray();
@endphp

6.5 :highlight Pattern

{!! cms('welcome.solutions.heading', [
    'highlight' => '<span class="text-gradient-premium">'
        . cms('welcome.solutions.heading_highlight') . '</span>',
]) !!}

7. Tests einrichten

7.1 Test-Dateien kopieren

cp -r package/flux-cms/core/tests-reference/Feature/Cms tests/Feature/Cms

7.2 Tests ausführen

php artisan test --filter=Cms

8. Anpassungen für dein Projekt

Sprachen hinzufügen

config/flux-cms.php:

'locales' => [
    'de' => 'Deutsch',
    'en' => 'English',
    'fr' => 'Français',
],

Neue Content-Typen

  1. Migration + Model mit HasTranslations
  2. Admin-View als Volt-Komponente
  3. Route registrieren, Sidebar-Link hinzufügen
  4. toFrontendArray() Methode für Frontend-Integration
  5. Seeder erstellen

Editor-Toolbar

  • Standard (bold italic): Normale Texte
  • Voll (heading | bold italic underline strike | bullet ordered blockquote | link): Impressum, Datenschutz, News-Body

Icon-Auswahl (Performance)

Die Icon-Auswahl nutzt Blade Heroicons. Nur der Name wird im Dropdown angezeigt, das SVG nur als Vorschau neben dem Select-Feld — damit werden nicht 2800+ SVGs gerendert.


Verzeichnisstruktur nach Installation

dein-projekt/
├── app/
│   ├── helpers.php                       # cms(), tcms(), cms_media_url(), media_url()
│   └── Livewire/Admin/Cms/
│       ├── MediaLibraryUploader.php      # Multi-File Upload
│       └── MediaPicker.php               # Medienauswahl-Modal
├── config/
│   └── flux-cms.php                      # CMS + Media Konfiguration
├── database/
│   ├── migrations/
│   │   └── *_create_flux_cms_*.php       # Automatisch vom Package
│   └── seeders/
│       ├── CmsContentSeeder.php          # Lang → DB
│       ├── CmsMediaSeeder.php            # Medien + Image-Content
│       ├── CmsDownloadSeeder.php         # Case Studies etc.
│       ├── CmsNewsItemSeeder.php
│       ├── CmsIndustrySeeder.php
│       ├── CmsFaqSeeder.php
│       └── CmsLinkedinPostSeeder.php
├── package/flux-cms/core/                # Das Package
├── resources/views/
│   ├── components/layouts/
│   │   └── cms.blade.php                # Admin-Layout
│   └── livewire/admin/cms/
│       ├── content-index.blade.php       # Content-Editor
│       ├── media-index.blade.php         # Medienbibliothek
│       ├── media-library-uploader.blade.php
│       ├── media-picker.blade.php
│       ├── news-index.blade.php
│       ├── downloads-index.blade.php
│       ├── team-index.blade.php
│       ├── linkedin-index.blade.php
│       ├── industries-index.blade.php
│       ├── faqs-index.blade.php
│       ├── search-index.blade.php        # Suchindex-Verwaltung
│       └── dashboard.blade.php           # CMS-Dashboard
└── routes/web.php                        # CMS-Routes