| .. | ||
| components | ||
| core | ||
| starter-components | ||
| ARCHITECTURE.md | ||
| CONTRIBUTING.md | ||
| INSTALLATION.md | ||
| LICENSE.md | ||
| MIGRATION.md | ||
| README-FILE-UPLOAD.md | ||
| README.md | ||
| SETUP.md | ||
Flux CMS — Content Management für Laravel + Livewire + Flux UI
Überblick
Flux CMS ist ein modulares, mehrsprachiges Content-Management-System für Laravel-Anwendungen. Es nutzt Livewire Volt (Functional API) und Flux UI Pro als Admin-Oberfläche und speichert alle Inhalte übersetzbar in der Datenbank (spatie/laravel-translatable).
Kernkonzept: Bestehende Inhalte aus Laravel lang/-Dateien werden in die Datenbank migriert. Ein cms() Helper mit Fallback auf __() sorgt für nahtlose Migration — Seiten funktionieren sofort, auch wenn noch nicht alle Inhalte in der DB sind.
Tech-Stack
- Laravel 12, Livewire 3, Volt (Functional API)
- Flux UI Pro v2 (Komponenten-Bibliothek)
- Tailwind CSS v4
spatie/laravel-translatablefür Mehrsprachigkeitintervention/imagev3 für Bildverarbeitung- Pest v4 für Tests
Architektur
┌──────────────────────────────────────────────────────┐
│ Frontend (Blade/Livewire Views) │
│ {{ cms('welcome.hero.heading') }} │
│ {{ cms_media_url('welcome.hero.image') }} │
│ {{ media_url('keyvisual.webp', 'hero') }} │
└──────────────┬───────────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────────┐
│ Helper-Funktionen │
│ cms() → CmsContentService (DB + Cache) │
│ cms_media_url() → CmsContent → CmsMedia → URL │
│ media_url() → CmsMedia → URL (mit Cache) │
└──────────────┬───────────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────────┐
│ Datenbank (flux_cms_* Tabellen) │
│ CmsContent, CmsMedia, CmsNewsItem, CmsIndustry, │
│ CmsFaq, CmsLinkedinPost, CmsDownload │
└──────────────┬───────────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────────┐
│ Media Library (Storage / public disk) │
│ cms/media/originals/ → Original-Uploads │
│ cms/media/conversions/ → Generierte Bildgrößen │
│ cms/media/thumbnails/ → Auto-generierte Thumbnails │
└──────────────┬───────────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────────┐
│ Admin UI (/admin/cms/*) │
│ Livewire Volt + Flux UI Pro │
│ Content-Editor, Medienbibliothek, Downloads, │
│ Team, News, LinkedIn, FAQs, Industries │
└──────────────────────────────────────────────────────┘
Datenbankmodelle
CmsContent (Kern)
Flexibler Key-Value-Store für alle Seiteninhalte.
| Feld | Typ | Beschreibung |
|---|---|---|
group |
string | Seiten-/Dateiname (z.B. welcome, about, footer) |
key |
string | Dot-Notation-Pfad (z.B. hero.heading, cta.description) |
type |
enum | text, html, image, json, link |
value |
JSON | Übersetzbar: {"de": "...", "en": "..."} |
order |
int | Sortierung (chronologisch nach Dateistruktur) |
CmsMedia (Medienbibliothek)
Zentrale Verwaltung aller Bilder und PDFs.
| Feld | Typ | Beschreibung |
|---|---|---|
filename |
string | Original-Dateiname (z.B. keyvisual.webp) |
path |
string | Storage-Pfad (z.B. cms/media/originals/abc123.webp) |
type |
string | image, pdf, document |
mime_type |
string | MIME-Typ |
file_size |
int | Dateigröße in Bytes |
original_width |
int | Breite bei Bildern |
original_height |
int | Höhe bei Bildern |
disk |
string | Storage-Disk (default: public) |
collection |
string | Optionale Sammlung/Ordner |
conversions |
JSON | Generierte Bildgrößen-Pfade |
title |
JSON | Übersetzbar |
alt_text |
JSON | Übersetzbar |
is_published |
bool | Veröffentlichungsstatus |
CmsDownload (Case Studies, Capabilities, Success Stories)
Vollständiges Download-Management mit sprachspezifischen PDFs.
| Feld | Typ | Beschreibung |
|---|---|---|
title |
JSON | Übersetzbar |
description |
JSON | Übersetzbar |
category |
string | case_study, capability, success_story |
icon |
string | Heroicon-Name |
sub_category |
string | Detail-Kategorie (z.B. "R&D Product Support") |
type_label |
JSON | Übersetzbar (z.B. "Case Study" / "Capability") |
alt |
JSON | Übersetzbar: Alt-Text für Vorschaubild |
file_path |
JSON | Übersetzbar: PDF-Dateiname pro Sprache (DE/EN) |
thumbnail |
string | Vorschaubild-Dateiname (CmsMedia) |
open_text |
JSON | Übersetzbar: Button-Text "PDF öffnen" |
download_text |
JSON | Übersetzbar: Button-Text "PDF downloaden" |
highlights |
JSON | Kennzahlen [{"value": "100%", "label": "..."}] |
checkpoints |
JSON | Checkpunkte [{"value": "..."}] |
Weitere Modelle
| Modell | Tabelle | Zweck |
|---|---|---|
CmsNewsItem |
flux_cms_news_items |
News-Band mit Icon, PDF, Datum, Bild (via CmsMedia) |
CmsIndustry |
flux_cms_industries |
Industries-Band mit Sortierung |
CmsFaq |
flux_cms_faqs |
FAQ-Einträge mit Kategorie und Hilfe-Text |
CmsLinkedinPost |
flux_cms_linkedin_posts |
LinkedIn-Posts mit Bild (via CmsMedia) |
CmsSearchIndex |
flux_cms_search_index |
Seitensuche: Keywords, Titel, Beschreibungen pro Route |
Alle Modelle nutzen Spatie\Translatable\HasTranslations für DE/EN (erweiterbar).
CmsSearchIndex (Seitensuche)
Zentrales Index-Modell für die Frontend-Suche. Jeder Eintrag repräsentiert eine Seite/Route.
| Feld | Typ | Beschreibung |
|---|---|---|
item_id |
string | Eindeutige ID (z.B. home, leistungen) |
route |
string | Laravel Named Route (z.B. home, leistungen) |
route_params |
JSON | Route-Parameter als Array |
category |
JSON | Übersetzbar: Kategorie (z.B. Startseite, Leistungen) |
title_key |
string | CMS-Key für Titel (löst via cms() auf) |
title_fallback |
JSON | Übersetzbar: Statischer Fallback-Titel |
description_key |
string | CMS-Key für Beschreibung |
description_fallback_key |
string | Sekundärer CMS-Key als Fallback |
description_fallback_text |
JSON | Übersetzbar: Statischer Fallback-Text |
keywords |
JSON | Übersetzbar: Array aus Keywords oder CMS-Keys (Dot-Notation) |
is_published |
bool | Veröffentlichungsstatus |
Keywords: Können direkte Texte oder CMS-Keys (mit Punkt) sein. CMS-Keys werden automatisch aufgelöst.
// toFrontendArray() löst alle Keys auf
$entries = CmsSearchIndex::published()->ordered()->get()
->map(fn ($entry) => $entry->toFrontendArray())
->toArray();
Medienbibliothek
Konzept
Alle Bilder und PDFs werden zentral in einer Medienbibliothek verwaltet. Anstatt Dateien direkt an Inhalte hochzuladen, werden sie aus der Bibliothek ausgewählt. Vorteile:
- Zentrale Verwaltung aller Assets
- Automatische Bildoptimierung (Resize, Kompression, Formatkonvertierung)
- Wiederverwendung von Medien über verschiedene Inhalte hinweg
- Sprachunabhängige Bilder, sprachspezifische PDFs
Bildprofile (Conversions)
Definiert in config/flux-cms.php:
'media' => [
'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],
],
],
Conversions werden automatisch beim Upload (Thumbnail) oder on-demand im Admin generiert.
MediaConversionService
$service = app(MediaConversionService::class);
// Einzelne Conversion
$service->convert($media, 'hero');
// Alle Conversions
$service->generateAllConversions($media);
// Thumbnail automatisch
$service->generateThumbnail($media);
Komponenten
| Komponente | Typ | Beschreibung |
|---|---|---|
MediaLibraryUploader |
Class-based Livewire | Multi-File Drag&Drop Upload via flux:file-upload |
MediaPicker |
Class-based Livewire | Wiederverwendbares Modal zur Medienauswahl |
MediaPicker-Verwendung:
<livewire:admin.cms.media-picker
:value="$imageMediaId"
field="news_image"
type="image"
profile="news"
label="Bild wählen"
:key="'img-' . $editingId" />
Feuert media-selected Event mit field, mediaId, url:
on(['media-selected' => function (string $field, ?int $mediaId, ?string $url) {
if ($field === 'news_image') {
$this->imageMediaId = $mediaId;
$media = $mediaId ? CmsMedia::find($mediaId) : null;
$this->image = $media ? $media->filename : '';
}
}]);
Helper-Funktionen
cms() — Content abrufen
$value = cms('welcome.hero.heading');
$value = cms('key', ['highlight' => '<span>...</span>']);
$value = cms('key', locale: 'en');
Fallback-Kette: DB (mit Cache) → __() (Laravel-Translation)
cms_media_url() — Bild über CmsContent-Key
// Holt Dateiname aus CmsContent, löst über CmsMedia auf
$url = cms_media_url('welcome.hero.image');
$url = cms_media_url('welcome.hero.image', 'hero'); // mit Conversion-Profil
media_url() — Bild über Dateiname
// Löst CmsMedia-Dateiname direkt auf (mit In-Memory-Cache)
$url = media_url('keyvisual.webp');
$url = media_url('keyvisual.webp', 'hero'); // mit Conversion-Profil
Fallback: asset('assets/images/' . $filename) wenn nicht in CmsMedia.
tcms() — Content mit Tooltip-Verarbeitung
$text = tcms('welcome.hero.heading');
Dateien-Übersicht
Package-Kern (package/flux-cms/core/)
src/
├── Models/
│ ├── CmsContent.php # Key-Value Content-Store
│ ├── CmsMedia.php # Medienbibliothek-Einträge
│ ├── CmsNewsItem.php # News (mit toFrontendArray())
│ ├── CmsDownload.php # Downloads (mit toFrontendArray())
│ ├── CmsIndustry.php # Industries
│ ├── CmsFaq.php # FAQs
│ └── CmsLinkedinPost.php # LinkedIn-Posts
├── Services/
│ ├── CmsContentService.php # Content-Abruf mit Caching + Fallback
│ └── MediaConversionService.php # Bildverarbeitung (intervention/image)
├── Helpers/
│ ├── cms_helpers.php # cms() und tcms() Helper
│ ├── MediaLibraryUploader.php # Multi-File Upload Komponente
│ └── MediaPicker.php # Medienauswahl-Modal
└── FluxCmsServiceProvider.php # Package-Registrierung
database/
├── migrations/
│ ├── ..._create_flux_cms_contents_table.php
│ ├── ..._create_flux_cms_downloads_table.php
│ ├── ..._create_flux_cms_linkedin_posts_table.php
│ ├── ..._create_flux_cms_faqs_table.php
│ ├── ..._create_flux_cms_news_items_table.php
│ ├── ..._create_flux_cms_industries_table.php
│ └── ..._create_flux_cms_media_table.php
└── seeders-reference/
config/
└── flux-cms.php # Locales, Cache, Media-Profiles, Auth
Projekt-Integration (Host-App)
app/
├── helpers.php # cms(), tcms(), cms_media_url(), media_url()
└── Livewire/Admin/Cms/
├── MediaLibraryUploader.php # Multi-File Upload
└── MediaPicker.php # Medienauswahl-Modal
resources/views/
├── livewire/admin/cms/ # Volt-Admin-Komponenten
│ ├── content-index.blade.php # Inhalte-Editor (text/html/image/json)
│ ├── news-index.blade.php # News-Verwaltung
│ ├── downloads-index.blade.php # Downloads (Case Studies/Capabilities/Stories)
│ ├── team-index.blade.php # Team-Verwaltung
│ ├── linkedin-index.blade.php # LinkedIn-Posts
│ ├── industries-index.blade.php # Industries
│ ├── faqs-index.blade.php # FAQ-Verwaltung
│ ├── media-index.blade.php # Medienbibliothek
│ ├── media-library-uploader.blade.php
│ ├── media-picker.blade.php
│ └── dashboard-index.blade.php # CMS-Dashboard
└── components/layouts/
└── cms.blade.php # CMS-Admin-Layout mit Sidebar
database/
├── migrations/
│ ├── ..._add_detail_columns_to_flux_cms_downloads_table.php
│ └── ..._change_file_path_to_json_on_flux_cms_downloads_table.php
└── seeders/
├── CmsContentSeeder.php # Lang → DB Migration
├── CmsMediaSeeder.php # Medien-Einträge + CmsContent image-Typen
├── CmsDownloadSeeder.php # Case Studies, Capabilities, Success Stories
├── CmsNewsItemSeeder.php # News-Band (mit Media-Filename-Mapping)
├── CmsLinkedinPostSeeder.php # LinkedIn-Posts (mit Media-Filenames)
├── CmsIndustrySeeder.php
└── CmsFaqSeeder.php
routes/web.php # CMS-Routes unter /admin/cms/*
Admin-Bereiche
Inhalte (/admin/cms/content)
- Alle
CmsContent-Einträge nach Gruppen - Text/HTML: Inline-Editor (Flux Editor) mit konfigurierbaren Toolbars
- Image: MediaPicker mit Vorschau
- JSON: Modal-Editor mit strukturierter Bearbeitung (Icons, Editor, Inputs)
- Sprachumschaltung DE/EN
Medienbibliothek (/admin/cms/media)
- Grid- und Listenansicht (Toggle)
- Filter nach Typ (Bilder/PDFs/Dokumente), Sammlung, Dateiname
- Multi-File Drag&Drop Upload
- Detail-Sidebar: Metadaten, Alt-Text, Titel, Sammlung
- Bildgrößen-Management: Einzelne oder alle Conversions generieren
- PDF-Vorschau: Eingebettete PDF-Darstellung via iframe
- Typ-Icons: Farbige Icons (blau=Bild, rot=PDF) neben Dateinamen
Downloads (/admin/cms/downloads)
- Drei Kategorien: Case Studies, Capabilities, Success Stories
- Kategoriefilter-Buttons
- Vollständige Bearbeitung: Titel, Unterkategorie, Typ-Label, Alt-Text, Icon, Beschreibung
- Vorschaubild: MediaPicker mit Bildvorschau
- Sprachspezifische PDFs: Separater PDF-Upload pro Sprache (DE/EN) mit Vorschau
- Highlights-Editor (Case Studies/Success Stories): Dynamische Kennzahl-Paare
- Checkpoints-Editor (Capabilities): Dynamische Checkpunkte
- Sortierung mit Pfeiltasten
toFrontendArray()für nahtlose Frontend-Integration
News-Band (/admin/cms/news)
- CRUD mit Icon (Heroicon-Select + Vorschau), Datum, Autor
- Bild: MediaPicker mit Vorschau in Liste und Formular
- PDF: MediaPicker mit eingebetteter PDF-Vorschau
- Rich-Text-Editor für Kurztext und Inhalt
- Sprachumschaltung
Team (/admin/cms/team)
- CRUD für Teammitglieder (JSON in CmsContent)
- Profilbild: MediaPicker mit Vorschau (rund)
- Name, Position, Kürzel, LinkedIn-URL, Kurzvorstellung
- Profil-Text mit Rich-Text-Editor
LinkedIn-Posts (/admin/cms/linkedin)
- CRUD mit Bild über MediaPicker (nicht mehr Text-Input)
- Titel, Autor, Datum, URL, Tags
- Rich-Text-Editor für Kurztext und Inhalt
- Bildvorschau in Liste und Formular
Industries (/admin/cms/industries)
- CRUD mit Sortierung (Pfeiltasten + numerisches Order-Feld)
FAQs (/admin/cms/faqs)
- CRUD nach Kategorien
- Rich-Text-Editor für Frage und Antwort
Suchindex (/admin/cms/search-index)
- Zwei-Spalten-Layout: Liste links, Editor rechts
- Jeder Eintrag = eine Seite/Route (z.B.
home,leistungen) - Keywords können direkte Texte oder CMS-Keys (Dot-Notation) sein
- Reindexieren-Button löst
search:extract-keywordsArtisan-Befehl aus - Vorschau zeigt aufgelöste Titel/Beschreibung/Keywords pro Sprache
- Sprachumschaltung DE/EN
Content-Typen & Editor-Konfiguration
Text-Felder
Standard-Editor mit bold italic Toolbar. Im Admin automatisch erkannt.
HTML-Felder (Datenschutz, Impressum)
Voller Editor mit heading | bold italic underline strike | bullet ordered blockquote | link.
Image-Felder
MediaPicker mit Vorschau. Speichert CmsMedia-Dateiname als Wert.
JSON-Felder
Öffnen ein Modal mit strukturierter Bearbeitung:
- Erkennung ob String-Array oder Objekt-Array
- Icon-Felder → Searchable Heroicon-Select (nur Vorschau des gewählten Icons)
- Text-Felder (description, content, quote) → Rich-Text-Editor
- Verschachtelte JSON → Textarea
- Standard-Felder → Input
:highlight Pattern
Für gradient-gestylte Textteile in Headings:
// Template
{!! cms('welcome.solutions.heading', [
'highlight' => '<span class="text-gradient-premium">' . cms('welcome.solutions.heading_highlight') . '</span>',
]) !!}
Medien-Integration im Frontend
Bilder über CmsContent-Keys (dynamisch austauschbar)
<img src="{{ cms_media_url('welcome.hero.image') }}" />
<img src="{{ cms_media_url('welcome.hero.image', 'hero') }}" /> {{-- mit Conversion --}}
Bilder über Dateiname (direkt)
<img src="{{ media_url('keyvisual.webp') }}" />
<img src="{{ media_url($profile['image']) }}" /> {{-- z.B. Team-Profil --}}
Downloads (toFrontendArray)
@foreach (CmsDownload::published()->byCategory('case_study')->ordered()->get() as $dl)
<x-download-article-card :article="$dl->toFrontendArray()" :index="$loop->index" />
@endforeach
News-Band (toFrontendArray)
@php
$newsItems = CmsNewsItem::published()->ordered()->get()
->map(fn ($item) => $item->toFrontendArray())
->toArray();
@endphp
Die toFrontendArray() Methoden lösen media_url() automatisch auf — Bilder und PDFs sind sofort verlinkt.
Seeders
Ausführungsreihenfolge
$this->call([
CmsContentSeeder::class, // Hauptinhalte aus lang/ Dateien
CmsMediaSeeder::class, // Medien-Einträge + Image-Content
CmsNewsItemSeeder::class, // News-Band
CmsIndustrySeeder::class, // Industries
CmsFaqSeeder::class, // FAQs
CmsLinkedinPostSeeder::class, // LinkedIn-Posts
CmsDownloadSeeder::class, // Case Studies, Capabilities, Stories
]);
Medien-Seeder
Der CmsMediaSeeder erfüllt zwei Aufgaben:
- Erstellt
CmsMedia-Einträge für alle hochgeladenen Dateien (Bilder + PDFs) - Erstellt
CmsContent-Einträge vom Typimage(z.B.welcome.hero.image→keyvisual-small.webp)
Download-Seeder
Enthält inline alle 12 Einträge (4 Case Studies, 5 Capabilities, 3 Success Stories) mit:
- Bilingualen Texten (DE/EN)
- Sprachspezifischen PDF-Dateinamen (DE/EN)
- Media-Dateinamen (statt alter Pfade)
- Highlights / Checkpoints
Dateiname-Mapping
Die Seeder für News und LinkedIn mappen alte Pfade auf Media-Dateinamen:
// Alt: '/assets/images/capability-global-player.jpg'
// Neu: 'capability-global-player.webp' (CmsMedia-Dateiname)
Installation
→ Siehe SETUP.md für eine vollständige Schritt-für-Schritt-Anleitung. Für die Migration in ein bestehendes Projekt: MIGRATION.md.
Kurzfassung
- Package einbinden (
composer.jsonRepository + require) composer update flux-cms/corephp artisan vendor:publish --tag=flux-cms-configphp artisan migrate- Helper-Funktionen in
app/helpers.php+composer.jsonautoload - Admin-Views aus
admin-reference/kopieren - Seeders kopieren & ausführen
- Routes registrieren
intervention/imageinstallieren:composer require intervention/image
Features-Übersicht
| Feature | Status | Beschreibung |
|---|---|---|
| Mehrsprachige Inhalte | ✅ | DE/EN, erweiterbar |
| Content-Editor (Text/HTML/Image) | ✅ | Flux Editor mit konfigurierbaren Toolbars + MediaPicker |
| JSON-Modal-Editor | ✅ | Strukturierte Bearbeitung von Arrays |
| Heroicon-Select | ✅ | Searchable mit Vorschau (performant) |
| Medienbibliothek | ✅ | Zentrale Bild/PDF-Verwaltung mit Grid+Listenansicht |
| Bildoptimierung | ✅ | Automatische Conversions (Resize/Format/Qualität) |
| MediaPicker | ✅ | Wiederverwendbares Modal für Medienauswahl |
| PDF-Vorschau | ✅ | Eingebettete PDF-Darstellung im Admin |
| Downloads (erweitert) | ✅ | Case Studies, Capabilities, Success Stories mit Highlights/Checkpoints |
| Sprachspezifische PDFs | ✅ | Separate DE/EN PDFs pro Download |
| News-Verwaltung | ✅ | CRUD mit MediaPicker für Bild + PDF |
| Team-Verwaltung | ✅ | CRUD mit MediaPicker für Profilbilder |
| LinkedIn-Posts | ✅ | CRUD mit MediaPicker für Bilder |
| Industries-Verwaltung | ✅ | CRUD mit Sortierung |
| FAQ-Verwaltung | ✅ | CRUD nach Kategorien |
| Toast-Benachrichtigungen | ✅ | Flux Toast bei allen Aktionen |
| Fallback auf lang/ | ✅ | cms() → __() wenn nicht in DB |
| Cache | ✅ | Pro Gruppe, auto-invalidiert |
| Seeder mit cleanHtml() | ✅ | Automatische HTML-Bereinigung |
| Media-Seeder | ✅ | Wiederherstellung aller Medien nach DB-Reset |
| Suchindex | ✅ | Keywords, Titel, Beschreibungen pro Route mit CMS-Key-Auflösung |
Offene Punkte / Roadmap
- Drag & Drop Sortierung (Alternative zu Pfeilen)
- Versionierung von Content-Änderungen
- Rollen-basierter Zugriff auf CMS-Bereiche
- Import/Export von Inhalten
- Automatischer LinkedIn-API-Import
- Bildbearbeitung (Crop/Rotate) im Admin