b2in/packages/flux-cms/README.md
2025-10-20 17:50:35 +02:00

13 KiB

Flux CMS - Laravel Package Suite

🚀 Modern, component-first CMS for Laravel with multi-domain support

Latest Version Software License Total Downloads

Overview

Flux CMS is a powerful, modern Content Management System built specifically for Laravel applications. It features a revolutionary "Code-as-Schema" approach where content structure is defined directly in PHP components, offering unprecedented flexibility and developer experience.

🎯 Key Features

  • 🧩 Component-First Architecture - Build pages from reusable Livewire components
  • 📝 Code-as-Schema - Define fields in PHP, not in databases
  • 🌍 Multi-Domain Support - Manage multiple websites from one installation
  • 🗣️ Full Multilingual - Everything is translatable with fallbacks
  • 📦 Versioning - Automatic content versioning and rollback
  • 🎨 Media Management - Integrated media library with automatic conversions
  • Performance - Optimized queries and smart caching
  • 🔒 Secure - Built-in security best practices

Package Architecture

Flux CMS is split into modular packages for maximum flexibility:

Core Packages

Package Description Installation
flux-cms/core Core models, services, and field types composer require flux-cms/core
flux-cms/components Livewire backend and frontend components composer require flux-cms/components
flux-cms/starter-components Ready-to-use starter components composer require flux-cms/starter-components

Quick Start

1. Installation

# Install core package
composer require flux-cms/core

# Install components (optional but recommended)
composer require flux-cms/components flux-cms/starter-components

# Install and setup
php artisan flux-cms:install

2. Basic Configuration

// config/flux-cms.php
return [
    'locales' => [
        'de' => 'Deutsch',
        'en' => 'English',
    ],
    'component_paths' => [
        'App\\Livewire\\Components',
        'FluxCms\\StarterComponents\\Components',
    ],
    'domains' => [
        'enabled' => true,
        'config_source' => 'domains', // Use existing config/domains.php
    ],
];

3. Create Your First Component

<?php

namespace App\Livewire\Components;

use Livewire\Component;
use FluxCms\Core\FieldTypes\TextField;
use FluxCms\Core\FieldTypes\WysiwygField;
use FluxCms\Core\FieldTypes\MediaField;

class FeatureSection extends Component
{
    public array $content = [];

    public function mount(array $content = [])
    {
        $this->content = $content;
    }

    public static function getCmsName(): string
    {
        return 'Feature Section';
    }

    public static function getCmsDescription(): string
    {
        return 'Showcase features with icons and descriptions';
    }

    public static function getCmsCategory(): string
    {
        return 'Content';
    }

    public static function getCmsFields(): array
    {
        return [
            TextField::make('headline', 'Headline')
                ->translatable()
                ->required()
                ->maxLength(100),

            WysiwygField::make('description', 'Description')
                ->translatable()
                ->toolbar(['bold', 'italic', 'link']),

            MediaField::make('icon', 'Icon')
                ->images()
                ->helpText('SVG or PNG icon'),
        ];
    }

    public function render()
    {
        return view('components.feature-section');
    }

    // Helper methods
    protected function getHeadline(?string $locale = null): string
    {
        $locale = $locale ?? app()->getLocale();
        return $this->content['headline'][$locale] ?? '';
    }
}

4. Create the Blade Template

{{-- resources/views/components/feature-section.blade.php --}}
<section class="feature-section py-12">
    <div class="container mx-auto px-4">
        @if($this->getHeadline())
            <h2 class="text-3xl font-bold text-center mb-8">
                {{ $this->getHeadline() }}
            </h2>
        @endif

        @if($this->getDescription())
            <div class="prose prose-lg mx-auto text-center">
                {!! $this->getDescription() !!}
            </div>
        @endif
    </div>
</section>

Advanced Usage

Multi-Domain Setup

// Automatic domain detection from existing config/domains.php
$page = Page::forDomain('b2in')->bySlug('/about', 'en')->first();

// Create domain-specific content
$page = Page::create([
    'domain_key' => 'b2in',
    'title' => ['de' => 'Über uns', 'en' => 'About us'],
    'is_published' => true,
]);

$page->slugs()->create(['locale' => 'de', 'slug' => '/ueber-uns']);
$page->slugs()->create(['locale' => 'en', 'slug' => '/about']);

Component Registry

// Get all available components
$registry = app(ComponentRegistry::class);
$components = $registry->getAvailableComponents();

// Search components
$results = $registry->searchComponents('hero');

// Get by category
$layoutComponents = $registry->getComponentsByCategory()['Layout'];

// Validate component content
$errors = $registry->validateComponentContent(HeroSection::class, $content);

Custom Field Types

<?php

namespace App\FieldTypes;

use FluxCms\Core\FieldTypes\BaseField;

class ColorField extends BaseField
{
    public function getType(): string
    {
        return 'color';
    }

    public function getValidationRules(): array
    {
        $rules = ['string', 'regex:/^#[0-9A-Fa-f]{6}$/'];

        if ($this->required) {
            $rules[] = 'required';
        }

        return $rules;
    }

    public function toArray(): array
    {
        return [
            'type' => $this->getType(),
            'key' => $this->key,
            'label' => $this->label,
            'translatable' => $this->translatable,
            'required' => $this->required,
            'default' => $this->default,
        ];
    }
}

Versioning

// Create version before major changes
$page->createVersion('Redesign homepage layout', auth()->id());

// Restore previous version
$version = $page->versions()->first();
$version->restore();

// Compare versions
$differences = $version->getDifferences();

Field Types

Flux CMS includes powerful field types out of the box:

Text Fields

TextField::make('title', 'Title')
    ->translatable()
    ->required()
    ->maxLength(100)
    ->placeholder('Enter title...');

TextField::make('email', 'Email')
    ->email()
    ->required();

TextField::make('website', 'Website')
    ->url();

Content Fields

WysiwygField::make('content', 'Content')
    ->translatable()
    ->toolbar(['bold', 'italic', 'link', 'bulletList'])
    ->allowImages(true)
    ->minHeight(300);

Media Fields

MediaField::make('image', 'Image')
    ->images()
    ->required();

MediaField::make('gallery', 'Gallery')
    ->images()
    ->multiple(true, 10);

MediaField::make('document', 'Document')
    ->documents()
    ->maxFileSize(5120); // 5MB

Selection Fields

SelectField::make('layout', 'Layout')
    ->options([
        'left' => 'Image Left',
        'right' => 'Image Right',
        'center' => 'Centered'
    ])
    ->default('left')
    ->searchable();

Other Fields

NumberField::make('count', 'Count')
    ->min(1)
    ->max(100)
    ->default(5);

BooleanField::make('featured', 'Featured')
    ->default(false)
    ->labels('Yes', 'No');

Frontend Integration

Page Controller

<?php

namespace App\Http\Controllers;

use FluxCms\Core\Models\Page;
use Illuminate\Http\Request;

class PageController extends Controller
{
    public function show(Request $request, string $slug = '/')
    {
        $domainKey = $this->getCurrentDomainKey($request);
        $locale = app()->getLocale();

        $page = Page::forDomain($domainKey)
                   ->bySlugWithFallback($slug)
                   ->published()
                   ->with(['components'])
                   ->firstOrFail();

        $components = $page->components()->get();

        return view('pages.show', compact('page', 'components'));
    }
}

Page Template

{{-- resources/views/pages/show.blade.php --}}
@extends('layouts.app')

@section('title', $page->getSeoTitle())
@section('description', $page->getSeoDescription())

@section('content')
    @foreach($components as $component)
        @if($component->canRender())
            @livewire($component->component_class, [
                'content' => $component->getTranslations('content')
            ], key('component-' . $component->id))
        @endif
    @endforeach
@endsection

Backend Integration

Admin Routes

// routes/admin.php
Route::middleware(['web', 'auth'])->prefix('admin/cms')->name('admin.cms.')->group(function () {
    Route::get('/', [Admin\DashboardController::class, 'index'])->name('index');
    Route::resource('pages', Admin\PageController::class)->except(['show']);
    // ... other admin routes
});

Admin Controller

<?php

namespace App\Http\Controllers\Admin;

use FluxCms\Core\Models\Page;
use Illuminate\Http\Request;

class PageController extends Controller
{
    public function index()
    {
        $pages = Page::with(['components'])
                    ->orderBy('updated_at', 'desc')
                    ->paginate(20);

        return view('admin.cms.pages.index', compact('pages'));
    }

    public function edit(Page $page)
    {
        return view('admin.cms.pages.edit', compact('page'));
    }
}

Edit Page Template

{{-- resources/views/admin/cms/edit.blade.php --}}
@extends('layouts.admin')

@section('content')
    @livewire('flux-cms::page-editor', ['page' => $page])
@endsection

Configuration

Available Locales

// config/flux-cms.php
'locales' => [
    'de' => 'Deutsch',
    'en' => 'English',
    'fr' => 'Français',
    'es' => 'Español',
],

Component Paths

'component_paths' => [
    'App\\Livewire\\Components',
    'App\\CmsComponents',
    'FluxCms\\StarterComponents\\Components',
],

Media Configuration

'media' => [
    'disk' => 'public',
    'max_file_size' => 10240, // 10MB
    'conversions' => [
        'thumb' => ['width' => 300, 'height' => 300],
        'medium' => ['width' => 800, 'height' => 600],
        'large' => ['width' => 1200, 'height' => 900],
    ],
],

Commands

# Install Flux CMS
php artisan flux-cms:install

# Create a new component (in app/Livewire/Web/Components)
php artisan flux-cms:make-component MyNewComponent

# Clear component registry cache
php artisan flux-cms:clear-cache

# Publish package assets
php artisan vendor:publish --tag=flux-cms

Testing

# Run tests from the root of your project
./vendor/bin/pest

Security

  • XSS Protection: All user content is sanitized
  • CSRF Protection: All forms include CSRF tokens
  • File Upload Security: MIME type validation and file scanning
  • Permission System: Integration with Spatie Laravel Permission

Performance

  • Component Registry Caching: Components are cached for fast lookup
  • Eager Loading: Optimized database queries
  • Asset Optimization: Automatic image conversions
  • Query Caching: Smart caching for frequently accessed data

Upgrade Guide

From Dev Version to Package

  1. Install packages:
composer require flux-cms/core flux-cms/components flux-cms/starter-components
  1. Migrate existing components:
# Copy your existing components to app/Livewire/Components/
# Update namespaces and field imports
  1. Update configuration:
php artisan vendor:publish --tag=flux-cms-config
  1. Run migrations:
php artisan migrate

Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

Development Setup

# Clone the repository
git clone https://github.com/flux-cms/flux-cms.git

# Install dependencies
composer install

# Run tests
composer test

# Code style
composer format

License

Flux CMS is open-sourced software licensed under the MIT license.

Support

Acknowledgments


Made with ❤️ by the Flux CMS team