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

14 KiB

Flux CMS Installation Guide

This guide will walk you through installing and setting up Flux CMS in your Laravel application.

System Requirements

  • PHP: 8.2 or higher
  • Laravel: 11.0 or higher
  • Livewire: 3.0 or higher
  • Database: MySQL 8.0+ or PostgreSQL 13+
  • Node.js: 18+ (for asset compilation)

Required Laravel Packages

Flux CMS requires these packages to be installed:

  • spatie/laravel-translatable: For multilingual content
  • spatie/laravel-medialibrary: For media management
  • spatie/laravel-tags: For tagging blog posts
  • livewire/livewire: For reactive components
  • livewire/flux: For UI components (recommended)

Step-by-Step Installation

1. Install Required Dependencies

# Install required packages
composer require spatie/laravel-translatable spatie/laravel-medialibrary spatie/laravel-tags

# Install Livewire if not already installed
composer require livewire/livewire

# Install Flux UI (recommended for admin interface)
composer require livewire/flux

2. Install Flux CMS Packages

# Install core package
composer require flux-cms/core

# Install components package (recommended)
composer require flux-cms/components

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

3. Run Installation Command

# This will publish config, run migrations, and set up permissions
php artisan flux-cms:install

# Or with options
php artisan flux-cms:install --no-migrate --no-publish

The installation command will:

  • Check system requirements
  • 📦 Publish configuration files
  • 🗃️ Run database migrations
  • 🔗 Create storage link
  • 📝 Create sample content (optional)
  • 🔐 Set up permissions (optional)

4. Configure Your Application

Environment Variables

Add these to your .env file:

# Flux CMS Configuration
FLUX_CMS_DEFAULT_LOCALE=de
FLUX_CMS_CACHE_ENABLED=true
FLUX_CMS_ROUTES_ENABLED=true

# Media Configuration
FLUX_CMS_MEDIA_DISK=public
FLUX_CMS_MAX_FILE_SIZE=10240

# Multi-domain (if using)
FLUX_CMS_MULTI_DOMAIN=true
FLUX_CMS_AUTO_DETECT_DOMAIN=true

Update App Configuration

// config/app.php
'locale' => env('APP_LOCALE', 'de'),
'fallback_locale' => 'de',

// Add available locales
'available_locales' => ['de', 'en'],

5. Set Up Routes

Admin Routes

Add to your routes/web.php or create routes/admin.php:

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

Frontend Routes

Add to your routes/web.php (MUST be at the end):

// SEO routes
Route::get('/sitemap.xml', [PageController::class, 'sitemap'])->name('sitemap');
Route::get('/robots.txt', [PageController::class, 'robots'])->name('robots');

// Blog routes (if using blog)
Route::prefix('blog')->name('blog.')->group(function () {
    Route::get('/', [PageController::class, 'blogIndex'])->name('index');
    Route::get('/{slug}', [PageController::class, 'blogPost'])->name('show');
});

// CMS pages - MUST BE LAST!
Route::get('/{slug?}', [PageController::class, 'show'])
     ->where('slug', '.*')
     ->name('cms.page');

6. Create Controllers

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 = '/')
    {
        // Get domain key from existing domains config
        $domainKey = $this->getCurrentDomainKey($request);
        $locale = app()->getLocale();

        // Find page
        $page = Page::forDomain($domainKey)
                   ->bySlug($slug, $locale)
                   ->published()
                   ->firstOrFail();

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

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

    public function sitemap(Request $request)
    {
        $domainKey = $this->getCurrentDomainKey($request);
        $pages = Page::forDomain($domainKey)->published()->get();

        return response()->view('sitemap', compact('pages'))
                        ->header('Content-Type', 'application/xml');
    }

    protected function getCurrentDomainKey(Request $request): string
    {
        $host = $request->getHost();
        $domains = config('domains.domains', []);

        foreach ($domains as $key => $config) {
            if (isset($config['url']) && parse_url($config['url'], PHP_URL_HOST) === $host) {
                return $key;
            }
        }

        return config('flux-cms.domains.default_domain', 'default');
    }
}

Admin Controller

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use FluxCms\Core\Models\Page;

class CmsController extends Controller
{
    public function __construct()
    {
        $this->middleware('can:flux-cms.view');
    }

    public function index()
    {
        $stats = [
            'pages' => Page::count(),
            'published_pages' => Page::published()->count(),
            'draft_pages' => Page::where('is_published', false)->count(),
        ];

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

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

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

    public function editPage(Page $page)
    {
        $this->authorize('flux-cms.edit');

        return view('admin.cms.edit-page', compact('page'));
    }
}

7. Create Views

Frontend Page Template

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

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

@push('meta')
    <meta property="og:title" content="{{ $page->getTranslation('title') }}">
    <meta property="og:description" content="{{ $page->getSeoDescription() }}">
    <meta property="og:url" content="{{ request()->url() }}">
    @if($page->getTranslation('og_image'))
        <meta property="og:image" content="{{ $page->getTranslation('og_image') }}">
    @endif
@endpush

@section('content')
    <main class="cms-page">
        @foreach($components as $component)
            @if($component->canRender())
                <div class="cms-component" data-component="{{ class_basename($component->component_class) }}">
                    @livewire($component->component_class, [
                        'content' => $component->getTranslations('content')
                    ], key('component-' . $component->id))
                </div>
            @endif
        @endforeach
    </main>
@endsection

Admin Views

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

@section('title', 'Edit Page: ' . $page->getTranslation('title'))

@section('content')
    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        @livewire('flux-cms::page-editor', ['page' => $page])
    </div>
@endsection

@push('scripts')
    <script>
        // Enable drag & drop sorting
        Livewire.on('components-reordered', (orderedIds) => {
            // Handle reordering feedback
            console.log('Components reordered:', orderedIds);
        });

        // Preview functionality
        Livewire.on('open-preview', (url) => {
            window.open(url, '_blank');
        });
    </script>
@endpush

8. Set Up Permissions

If you're using Spatie Laravel Permission:

// database/seeders/CmsPermissionSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class CmsPermissionSeeder extends Seeder
{
    public function run()
    {
        // Create permissions
        $permissions = [
            'flux-cms.view',
            'flux-cms.edit',
            'flux-cms.publish',
            'flux-cms.delete',
            'flux-cms.admin',
        ];

        foreach ($permissions as $permission) {
            Permission::firstOrCreate(['name' => $permission]);
        }

        // Create CMS role
        $cmsRole = Role::firstOrCreate(['name' => 'flux-cms']);
        $cmsRole->syncPermissions($permissions);

        // Assign to users
        // User::find(1)->assignRole('flux-cms');
    }
}

Run the seeder:

php artisan db:seed --class=CmsPermissionSeeder

9. Configure Multi-Domain (Optional)

If you're using multi-domain setup, ensure your config/domains.php is configured:

// config/domains.php
return [
    'domains' => [
        'main' => [
            'name' => 'Main Site',
            'url' => env('APP_URL', 'https://example.com'),
            'theme' => 'default',
        ],
        'blog' => [
            'name' => 'Blog Site',
            'url' => 'https://blog.example.com',
            'theme' => 'blog',
        ],
    ],
];

Update Flux CMS config:

// config/flux-cms.php
'domains' => [
    'enabled' => true,
    'config_source' => 'domains', // Use domains.php config
    'auto_detect' => true,
],

10. Create Your First Component

Generate a new component:

php artisan make:livewire Components/WelcomeHero

Update the component:

<?php

namespace App\Livewire\Components;

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

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

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

    public static function getCmsName(): string
    {
        return 'Welcome Hero';
    }

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

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

            WysiwygField::make('description', 'Description')
                ->translatable(),
        ];
    }

    public function render()
    {
        return view('livewire.components.welcome-hero');
    }
}

Create the view:

{{-- resources/views/livewire/components/welcome-hero.blade.php --}}
<section class="hero bg-gradient-to-r from-blue-500 to-purple-600 text-white py-20">
    <div class="container mx-auto px-4 text-center">
        @if($headline = $this->content['headline'][app()->getLocale()] ?? '')
            <h1 class="text-5xl font-bold mb-6">{{ $headline }}</h1>
        @endif

        @if($description = $this->content['description'][app()->getLocale()] ?? '')
            <div class="text-xl prose prose-lg prose-invert mx-auto">
                {!! $description !!}
            </div>
        @endif
    </div>
</section>

11. Create Your First Page

// database/seeders/CmsContentSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use FluxCms\Core\Models\Page;

class CmsContentSeeder extends Seeder
{
    public function run()
    {
        $homepage = Page::create([
            'domain_key' => 'main',
            'title' => [
                'de' => 'Startseite',
                'en' => 'Homepage'
            ],
            'slug' => [
                'de' => '/',
                'en' => '/'
            ],
            'meta_description' => [
                'de' => 'Willkommen auf unserer Website',
                'en' => 'Welcome to our website'
            ],
            'is_published' => true,
        ]);

        // Add welcome hero component
        $homepage->allComponents()->create([
            'component_class' => \App\Livewire\Components\WelcomeHero::class,
            'order' => 1,
            'content' => [
                'headline' => [
                    'de' => 'Willkommen bei Flux CMS',
                    'en' => 'Welcome to Flux CMS'
                ],
                'description' => [
                    'de' => 'Das moderne Content Management System für Laravel.',
                    'en' => 'The modern Content Management System for Laravel.'
                ]
            ],
        ]);
    }
}

Run the seeder:

php artisan db:seed --class=CmsContentSeeder

Verification

After installation, verify everything works:

  1. Visit admin interface: /admin/cms
  2. Edit a page: Click on your homepage
  3. Add components: Try adding components to your page
  4. Check frontend: Visit your homepage
  5. Test translations: Switch languages if configured

Troubleshooting

Common Issues

1. Component Registry Empty

# Clear cache and refresh
php artisan flux-cms:clear-cache
php artisan cache:clear

2. Permission Denied

# Check if user has CMS role
php artisan tinker
> User::find(1)->assignRole('flux-cms');

3. Media Files Not Loading

# Ensure storage link exists
php artisan storage:link

# Check disk configuration
php artisan tinker
> Storage::disk('public')->exists('test.txt');

4. Routes Not Working

  • Ensure CMS routes are at the end of routes/web.php
  • Check middleware and permissions
  • Verify domain configuration if using multi-domain

Debug Mode

Enable debug mode in config:

// config/flux-cms.php
'development' => [
    'debug_mode' => true,
    'show_component_info' => true,
    'log_queries' => true,
],

Getting Help

  • 📖 Documentation: Check the full documentation
  • 💬 Community: Join GitHub Discussions
  • 🐛 Issues: Report bugs on GitHub
  • 📧 Support: Contact support team

Next Steps

  1. Create custom components for your specific needs
  2. Set up navigation using the navigation manager
  3. Configure domains if using multi-domain
  4. Customize styling to match your brand
  5. Set up deployment with proper caching

You're now ready to start building amazing content with Flux CMS! 🚀