Files
Laravel-Docker-Dev-Template/scripts/laravel-setup.sh
2026-03-06 08:57:05 +02:00

1007 lines
30 KiB
Bash

#!/bin/bash
# Laravel Base Setup Script
# Run this after post-install.sh to configure auth, API, and base structure
# Usage: ./scripts/laravel-setup.sh
set -e
echo "=========================================="
echo "Laravel Base Setup"
echo "=========================================="
# Check if we're in a Laravel project
if [ ! -f "artisan" ]; then
echo "Error: Not in a Laravel project directory"
echo "Run this from your Laravel project root (src/)"
exit 1
fi
# ============================================
# AUTH SCAFFOLDING (Blade-focused, no JS frameworks)
# ============================================
echo ""
echo "============================================"
echo "AUTHENTICATION SETUP (Blade-based)"
echo "============================================"
echo ""
echo "Choose authentication scaffolding:"
echo ""
echo "1) Laravel Breeze + Blade (Recommended)"
echo " - Simple, server-side rendered"
echo " - Login, register, password reset, email verification"
echo " - Tailwind CSS styling"
echo ""
echo "2) Laravel Breeze + Livewire"
echo " - Reactive components without writing JavaScript"
echo " - Same features as Blade with dynamic updates"
echo ""
echo "3) Laravel Breeze API only"
echo " - Headless API authentication"
echo " - For when you build your own Blade views"
echo ""
echo "4) Laravel Jetstream + Livewire (Full-featured)"
echo " - Profile management, 2FA, API tokens"
echo " - Optional teams feature"
echo " - Best for SaaS applications"
echo ""
echo "5) None (Manual setup later)"
echo ""
read -p "Enter choice [1-5]: " AUTH_CHOICE
case $AUTH_CHOICE in
1)
echo ""
echo "Installing Breeze with Blade..."
composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install && npm run build
echo "Breeze (Blade) installed successfully!"
;;
2)
echo ""
echo "Installing Breeze with Livewire..."
composer require laravel/breeze --dev
php artisan breeze:install livewire
php artisan migrate
npm install && npm run build
echo "Breeze (Livewire) installed successfully!"
;;
3)
echo ""
echo "Installing Breeze API only..."
composer require laravel/breeze --dev
php artisan breeze:install api
php artisan migrate
echo "Breeze (API) installed successfully!"
echo "Build your own Blade views for the frontend."
;;
4)
echo ""
read -p "Enable Teams feature? [y/N]: " ENABLE_TEAMS
composer require laravel/jetstream
TEAMS_FLAG=""
if [[ "$ENABLE_TEAMS" =~ ^[Yy]$ ]]; then
TEAMS_FLAG="--teams"
fi
php artisan jetstream:install livewire $TEAMS_FLAG
php artisan migrate
npm install && npm run build
echo "Jetstream (Livewire) installed successfully!"
;;
5)
echo "Skipping auth scaffolding."
;;
esac
# ============================================
# TESTING (PEST)
# ============================================
echo ""
echo "============================================"
echo "TESTING FRAMEWORK (Pest)"
echo "============================================"
echo ""
echo "Pest is a testing framework with elegant syntax:"
echo " - Clean, readable test syntax"
echo " - Built on top of PHPUnit"
echo " - Great for unit & feature tests"
echo ""
read -p "Install Pest testing framework? [Y/n]: " INSTALL_PEST
if [[ ! "$INSTALL_PEST" =~ ^[Nn]$ ]]; then
echo ""
echo "Installing Pest..."
composer require pestphp/pest --dev --with-all-dependencies
composer require pestphp/pest-plugin-laravel --dev
# Initialize Pest
php artisan pest:install
# Configure phpunit.xml for SQLite in-memory
if [ -f "phpunit.xml" ]; then
# Add SQLite in-memory for testing
sed -i 's|<!-- <env name="DB_CONNECTION" value="sqlite"/> -->|<env name="DB_CONNECTION" value="sqlite"/>|' phpunit.xml
sed -i 's|<!-- <env name="DB_DATABASE" value=":memory:"/> -->|<env name="DB_DATABASE" value=":memory:"/>|' phpunit.xml
fi
# Create tests directory structure
mkdir -p tests/Feature
mkdir -p tests/Unit
mkdir -p tests/Modules
# Create Pest.php helper with useful traits
cat > tests/Pest.php << 'PEST'
<?php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
*/
uses(Tests\TestCase::class)->in('Feature', 'Modules');
uses(Tests\TestCase::class)->in('Unit');
/*
|--------------------------------------------------------------------------
| Database Refresh
|--------------------------------------------------------------------------
| Uses LazilyRefreshDatabase for faster tests - only migrates when needed.
*/
uses(LazilyRefreshDatabase::class)->in('Feature', 'Modules');
/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
*/
expect()->extend('toBeOne', function () {
return $this->toBe(1);
});
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
*/
function createUser(array $attributes = []): \App\Models\User
{
return \App\Models\User::factory()->create($attributes);
}
function createAdmin(array $attributes = []): \App\Models\User
{
$user = createUser($attributes);
$user->assignRole('admin');
return $user;
}
PEST
# Create example feature test
cat > tests/Feature/ExampleTest.php << 'TEST'
<?php
it('returns a successful response from homepage', function () {
$response = $this->get('/');
$response->assertStatus(200);
});
it('redirects guests from admin to login', function () {
$response = $this->get('/admin');
$response->assertRedirect('/admin/login');
});
TEST
# Create example unit test
cat > tests/Unit/ExampleTest.php << 'TEST'
<?php
it('can perform basic assertions', function () {
expect(true)->toBeTrue();
expect(1 + 1)->toBe(2);
expect(['a', 'b', 'c'])->toContain('b');
});
it('can use Laravel helpers', function () {
expect(config('app.name'))->toBeString();
expect(app())->toBeInstanceOf(\Illuminate\Foundation\Application::class);
});
TEST
# Create module test helper
cat > tests/Modules/.gitkeep << 'GITKEEP'
# Module tests go here
# Create subdirectories per module, e.g., tests/Modules/Inventory/
GITKEEP
# Create test helper for modules
cat > tests/TestHelpers.php << 'HELPER'
<?php
namespace Tests;
trait TestHelpers
{
/**
* Create a user with specific permissions.
*/
protected function userWithPermissions(array $permissions): \App\Models\User
{
$user = \App\Models\User::factory()->create();
foreach ($permissions as $permission) {
$user->givePermissionTo($permission);
}
return $user;
}
/**
* Create a user with module access.
*/
protected function userWithModuleAccess(string $moduleSlug): \App\Models\User
{
return $this->userWithPermissions([
"{$moduleSlug}.view",
"{$moduleSlug}.create",
"{$moduleSlug}.edit",
"{$moduleSlug}.delete",
]);
}
/**
* Assert model was audited.
*/
protected function assertAudited($model, string $event = 'created'): void
{
$this->assertDatabaseHas('audits', [
'auditable_type' => get_class($model),
'auditable_id' => $model->id,
'event' => $event,
]);
}
}
HELPER
echo ""
echo "Pest installed!"
echo "Run tests with: php artisan test"
echo "Or: ./vendor/bin/pest"
fi
# ============================================
# FILAMENT ADMIN PANEL
# ============================================
echo ""
echo "============================================"
echo "ADMIN PANEL (Filament)"
echo "============================================"
echo ""
echo "Filament provides a full-featured admin panel with:"
echo " - User management (list, create, edit, delete)"
echo " - Role & permission management"
echo " - Dashboard widgets"
echo " - Form & table builders"
echo ""
read -p "Install Filament Admin Panel? [Y/n]: " INSTALL_FILAMENT
if [[ ! "$INSTALL_FILAMENT" =~ ^[Nn]$ ]]; then
echo ""
echo "Installing Filament..."
composer require filament/filament:"^3.2" -W
php artisan filament:install --panels
echo ""
echo "Creating admin user..."
php artisan make:filament-user
# Create UserResource for user management
echo ""
echo "Creating User management resource..."
php artisan make:filament-resource User --generate
echo ""
echo "Filament installed successfully!"
echo "Admin panel available at: /admin"
# Install Site Settings
echo ""
echo "============================================"
echo "SITE SETTINGS (Appearance)"
echo "============================================"
echo ""
echo "Site settings allow you to manage:"
echo " - Logo and favicon"
echo " - Color scheme"
echo " - Site name"
echo ""
read -p "Install site settings? [Y/n]: " INSTALL_SETTINGS
if [[ ! "$INSTALL_SETTINGS" =~ ^[Nn]$ ]]; then
echo ""
echo "Installing spatie/laravel-settings..."
composer require spatie/laravel-settings
composer require filament/spatie-laravel-settings-plugin:"^3.2"
php artisan vendor:publish --provider="Spatie\LaravelSettings\LaravelSettingsServiceProvider" --tag="migrations"
php artisan migrate
# Create Settings directory
mkdir -p app/Settings
# Create SiteSettings class
cat > app/Settings/SiteSettings.php << 'SETTINGS'
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class SiteSettings extends Settings
{
public string $site_name;
public ?string $logo;
public ?string $favicon;
public string $primary_color;
public string $secondary_color;
public bool $dark_mode;
public ?string $footer_text;
public static function group(): string
{
return 'site';
}
}
SETTINGS
# Create settings migration
cat > database/settings/$(date +%Y_%m_%d_%H%M%S)_create_site_settings.php << 'MIGRATION'
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
return new class extends SettingsMigration
{
public function up(): void
{
$this->migrator->add('site.site_name', config('app.name', 'Laravel'));
$this->migrator->add('site.logo', null);
$this->migrator->add('site.favicon', null);
$this->migrator->add('site.primary_color', '#3b82f6');
$this->migrator->add('site.secondary_color', '#64748b');
$this->migrator->add('site.dark_mode', false);
$this->migrator->add('site.footer_text', null);
}
};
MIGRATION
# Run settings migration
php artisan migrate
# Create Filament Settings Page
mkdir -p app/Filament/Pages
cat > app/Filament/Pages/ManageSiteSettings.php << 'PAGE'
<?php
namespace App\Filament\Pages;
use App\Settings\SiteSettings;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Pages\SettingsPage;
class ManageSiteSettings extends SettingsPage
{
protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth';
protected static ?string $navigationGroup = 'Settings';
protected static ?int $navigationSort = 100;
protected static string $settings = SiteSettings::class;
protected static ?string $title = 'Site Settings';
protected static ?string $navigationLabel = 'Appearance';
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('General')
->description('Basic site information')
->schema([
Forms\Components\TextInput::make('site_name')
->label('Site Name')
->required()
->maxLength(255),
Forms\Components\FileUpload::make('logo')
->label('Logo')
->image()
->directory('site')
->visibility('public')
->imageResizeMode('contain')
->imageCropAspectRatio('16:9')
->imageResizeTargetWidth('400')
->helperText('Recommended: 400x100px or similar aspect ratio'),
Forms\Components\FileUpload::make('favicon')
->label('Favicon')
->image()
->directory('site')
->visibility('public')
->imageResizeTargetWidth('32')
->imageResizeTargetHeight('32')
->helperText('Will be resized to 32x32px'),
]),
Forms\Components\Section::make('Appearance')
->description('Customize the look and feel')
->schema([
Forms\Components\ColorPicker::make('primary_color')
->label('Primary Color')
->required(),
Forms\Components\ColorPicker::make('secondary_color')
->label('Secondary Color')
->required(),
Forms\Components\Toggle::make('dark_mode')
->label('Enable Dark Mode')
->helperText('Allow users to switch to dark mode'),
]),
Forms\Components\Section::make('Footer')
->schema([
Forms\Components\Textarea::make('footer_text')
->label('Footer Text')
->rows(2)
->placeholder('© 2024 Your Company. All rights reserved.'),
]),
]);
}
}
PAGE
# Create helper function
cat > app/Helpers/site.php << 'HELPER'
<?php
use App\Settings\SiteSettings;
if (!function_exists('site_settings')) {
function site_settings(?string $key = null, mixed $default = null): mixed
{
$settings = app(SiteSettings::class);
if ($key === null) {
return $settings;
}
return $settings->{$key} ?? $default;
}
}
if (!function_exists('site_logo')) {
function site_logo(): ?string
{
$logo = site_settings('logo');
return $logo ? asset('storage/' . $logo) : null;
}
}
if (!function_exists('site_favicon')) {
function site_favicon(): ?string
{
$favicon = site_settings('favicon');
return $favicon ? asset('storage/' . $favicon) : null;
}
}
if (!function_exists('site_name')) {
function site_name(): string
{
return site_settings('site_name', config('app.name'));
}
}
if (!function_exists('primary_color')) {
function primary_color(): string
{
return site_settings('primary_color', '#3b82f6');
}
}
if (!function_exists('secondary_color')) {
function secondary_color(): string
{
return site_settings('secondary_color', '#64748b');
}
}
HELPER
# Register helper in composer.json autoload
php -r "
\$composer = json_decode(file_get_contents('composer.json'), true);
\$composer['autoload']['files'] = \$composer['autoload']['files'] ?? [];
if (!in_array('app/Helpers/site.php', \$composer['autoload']['files'])) {
\$composer['autoload']['files'][] = 'app/Helpers/site.php';
}
file_put_contents('composer.json', json_encode(\$composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
"
composer dump-autoload
# Create Blade component
mkdir -p app/View/Components
cat > app/View/Components/SiteHead.php << 'COMPONENT'
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class SiteHead extends Component
{
public function __construct(
public ?string $title = null
) {}
public function render(): View|Closure|string
{
return view('components.site-head');
}
}
COMPONENT
mkdir -p resources/views/components
cat > resources/views/components/site-head.blade.php << 'BLADE'
{{-- Site Head Component - Include in your <head> --}}
<title>{{ $title ? $title . ' - ' . site_name() : site_name() }}</title>
@if(site_favicon())
<link rel="icon" href="{{ site_favicon() }}" type="image/x-icon">
@endif
<style>
:root {
--primary-color: {{ primary_color() }};
--secondary-color: {{ secondary_color() }};
}
.text-primary { color: var(--primary-color); }
.bg-primary { background-color: var(--primary-color); }
.border-primary { border-color: var(--primary-color); }
.text-secondary { color: var(--secondary-color); }
.bg-secondary { background-color: var(--secondary-color); }
.border-secondary { border-color: var(--secondary-color); }
/* Override Tailwind primary colors */
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
filter: brightness(0.9);
}
</style>
BLADE
echo ""
echo "Site settings installed!"
echo "Access at: /admin → Settings → Appearance"
fi
# Install Audit Trail
echo ""
echo "============================================"
echo "AUDIT TRAIL"
echo "============================================"
echo ""
echo "Audit trail tracks all changes to your data:"
echo " - Who made the change"
echo " - What was changed (old → new values)"
echo " - When it happened"
echo " - Which module/model"
echo ""
read -p "Install audit trail system? [Y/n]: " INSTALL_AUDIT
if [[ ! "$INSTALL_AUDIT" =~ ^[Nn]$ ]]; then
echo ""
echo "Installing owen-it/laravel-auditing..."
composer require owen-it/laravel-auditing
php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider" --tag="config"
php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"
php artisan migrate
# Install Filament Auditing plugin
echo ""
echo "Installing Filament audit trail UI..."
composer require tapp/filament-auditing:"^3.0"
php artisan vendor:publish --tag="filament-auditing-config"
# Create base Auditable trait for modules
mkdir -p app/Traits
cat > app/Traits/ModuleAuditable.php << 'TRAIT'
<?php
namespace App\Traits;
use OwenIt\Auditing\Contracts\Auditable;
trait ModuleAuditable
{
use \OwenIt\Auditing\Auditable;
/**
* Get the module name for this model.
* Override in your model to set custom module name.
*/
public function getModuleName(): string
{
// Extract module name from namespace
$namespace = get_class($this);
if (preg_match('/Modules\\\\([^\\\\]+)/', $namespace, $matches)) {
return $matches[1];
}
return 'Core';
}
/**
* Generate tags for the audit.
*/
public function generateTags(): array
{
return [
'module:' . $this->getModuleName(),
];
}
/**
* Attributes to include in the audit.
* Override in your model to customize.
*/
// protected $auditInclude = [];
/**
* Attributes to exclude from the audit.
* Override in your model to customize.
*/
// protected $auditExclude = [];
/**
* Audit strategy: 'all', 'include', 'exclude'
* Set via module config or model property.
*/
public function getAuditStrategy(): string
{
$moduleName = $this->getModuleName();
$configKey = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $moduleName));
return config("{$configKey}.audit.strategy", 'all');
}
/**
* Check if auditing is enabled for this model.
*/
public function isAuditingEnabled(): bool
{
$moduleName = $this->getModuleName();
$configKey = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $moduleName));
return config("{$configKey}.audit.enabled", true);
}
}
TRAIT
echo ""
echo "Audit trail installed!"
echo "Add 'use ModuleAuditable;' to models you want to audit."
fi
fi
# ============================================
# API SETUP (SANCTUM)
# ============================================
echo ""
echo "============================================"
echo "API SETUP"
echo "============================================"
echo ""
read -p "Configure Laravel Sanctum for API authentication? [Y/n]: " SETUP_SANCTUM
if [[ ! "$SETUP_SANCTUM" =~ ^[Nn]$ ]]; then
# Sanctum is included in Laravel 11+ by default
echo "Publishing Sanctum configuration..."
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
# Add Sanctum middleware to API
echo "Sanctum configured!"
echo ""
echo "API endpoints will use Sanctum for authentication."
echo "Tokens can be created via: \$user->createToken('token-name')"
fi
# ============================================
# STORAGE LINK
# ============================================
echo ""
echo "Creating storage symlink..."
php artisan storage:link
# ============================================
# BASE MIDDLEWARE
# ============================================
echo ""
echo "============================================"
echo "ADDITIONAL SETUP"
echo "============================================"
# Force HTTPS in production
read -p "Add ForceHttps middleware for production? [Y/n]: " ADD_HTTPS
if [[ ! "$ADD_HTTPS" =~ ^[Nn]$ ]]; then
mkdir -p app/Http/Middleware
cat > app/Http/Middleware/ForceHttps.php << 'MIDDLEWARE'
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ForceHttps
{
public function handle(Request $request, Closure $next): Response
{
if (app()->environment('production') && !$request->secure()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
MIDDLEWARE
echo "ForceHttps middleware created at app/Http/Middleware/ForceHttps.php"
echo "Register in bootstrap/app.php or routes to enable."
fi
# Security Headers Middleware
read -p "Add SecurityHeaders middleware? [Y/n]: " ADD_SECURITY
if [[ ! "$ADD_SECURITY" =~ ^[Nn]$ ]]; then
cat > app/Http/Middleware/SecurityHeaders.php << 'MIDDLEWARE'
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class SecurityHeaders
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
if (app()->environment('production')) {
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
return $response;
}
}
MIDDLEWARE
echo "SecurityHeaders middleware created at app/Http/Middleware/SecurityHeaders.php"
fi
# ============================================
# MODULE SYSTEM SETUP
# ============================================
echo ""
echo "============================================"
echo "MODULE SYSTEM"
echo "============================================"
echo ""
echo "The modular architecture allows you to organize features"
echo "into self-contained modules with their own admin panels."
echo ""
read -p "Install module system (spatie/laravel-permission + make:module command)? [Y/n]: " INSTALL_MODULES
if [[ ! "$INSTALL_MODULES" =~ ^[Nn]$ ]]; then
echo ""
echo "Installing Spatie Permission for role-based access..."
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
echo ""
echo "Setting up module system..."
# Create Modules directory
mkdir -p app/Modules
# Copy ModuleServiceProvider
if [ -f "../src/app/Providers/ModuleServiceProvider.php.stub" ]; then
cp ../src/app/Providers/ModuleServiceProvider.php.stub app/Providers/ModuleServiceProvider.php
else
# Create inline if stub not found
cat > app/Providers/ModuleServiceProvider.php << 'PROVIDER'
<?php
namespace App\Providers;
use Illuminate\Support\Facades\File;
use Illuminate\Support\ServiceProvider;
class ModuleServiceProvider extends ServiceProvider
{
public function register(): void
{
$modulesPath = app_path('Modules');
if (!File::isDirectory($modulesPath)) return;
foreach (File::directories($modulesPath) as $modulePath) {
$moduleName = basename($modulePath);
$providerClass = "App\\Modules\\{$moduleName}\\{$moduleName}ServiceProvider";
if (class_exists($providerClass)) {
$this->app->register($providerClass);
}
}
}
public function boot(): void
{
$modulesPath = app_path('Modules');
if (!File::isDirectory($modulesPath)) return;
foreach (File::directories($modulesPath) as $modulePath) {
$moduleName = basename($modulePath);
// Load routes
$webRoutes = "{$modulePath}/Routes/web.php";
$apiRoutes = "{$modulePath}/Routes/api.php";
if (File::exists($webRoutes)) $this->loadRoutesFrom($webRoutes);
if (File::exists($apiRoutes)) $this->loadRoutesFrom($apiRoutes);
// Load views
$viewsPath = "{$modulePath}/Resources/views";
if (File::isDirectory($viewsPath)) {
$slug = strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $moduleName));
$this->loadViewsFrom($viewsPath, $slug);
}
// Load migrations
$migrationsPath = "{$modulePath}/Database/Migrations";
if (File::isDirectory($migrationsPath)) {
$this->loadMigrationsFrom($migrationsPath);
}
}
}
}
PROVIDER
fi
# Copy MakeModule command
mkdir -p app/Console/Commands
if [ -f "../src/app/Console/Commands/MakeModule.php.stub" ]; then
cp ../src/app/Console/Commands/MakeModule.php.stub app/Console/Commands/MakeModule.php
fi
# Register ModuleServiceProvider in bootstrap/providers.php
if [ -f "bootstrap/providers.php" ]; then
if ! grep -q "ModuleServiceProvider" bootstrap/providers.php; then
sed -i 's/];/ App\\Providers\\ModuleServiceProvider::class,\n];/' bootstrap/providers.php
fi
fi
# Create base permission seeder
cat > database/seeders/PermissionSeeder.php << 'SEEDER'
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\File;
class PermissionSeeder extends Seeder
{
public function run(): void
{
// Create default roles
$adminRole = Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);
$userRole = Role::firstOrCreate(['name' => 'user', 'guard_name' => 'web']);
// Load permissions from all modules
$modulesPath = app_path('Modules');
if (File::isDirectory($modulesPath)) {
foreach (File::directories($modulesPath) as $modulePath) {
$permissionsFile = "{$modulePath}/Permissions.php";
if (File::exists($permissionsFile)) {
$permissions = require $permissionsFile;
foreach ($permissions as $name => $description) {
Permission::firstOrCreate(['name' => $name, 'guard_name' => 'web']);
}
// Give admin all module permissions
$adminRole->givePermissionTo(array_keys($permissions));
}
}
}
}
}
SEEDER
echo ""
echo "Module system installed!"
echo ""
echo "Create modules with: php artisan make:module ModuleName"
echo "Options:"
echo " --model=Product Create a model with Filament resource"
echo " --api Include API routes"
echo " --no-filament Skip Filament integration"
fi
# ============================================
# CACHE CONFIGURATION
# ============================================
echo ""
echo "Clearing and caching configuration..."
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear
# ============================================
# FINAL OUTPUT
# ============================================
echo ""
echo "=========================================="
echo "Laravel Base Setup Complete!"
echo "=========================================="
echo ""
echo "What was configured:"
echo " ✓ Authentication scaffolding (based on selection)"
echo " ✓ Filament Admin Panel (if selected)"
echo " ✓ Sanctum API authentication"
echo " ✓ Storage symlink"
echo " ✓ Security middleware"
echo " ✓ Module system (if selected)"
echo ""
echo "Next steps:"
echo " 1. Review and customize routes in routes/"
echo " 2. Add middleware to bootstrap/app.php if needed"
echo " 3. Create your first module: php artisan make:module YourModule"
echo " 4. Start building your application!"
echo ""
echo "Useful commands:"
echo " make up - Start development server"
echo " make test - Run tests"
echo " make lint - Fix code style"
echo " make fresh - Reset database with seeders"
echo " php artisan make:module Name - Create a new module"
echo ""