Module System

Understanding LaraDashboard's modular architecture powered by Laravel Modules. Learn how modules work, are loaded, and interact with the core system.

Module System

LaraDashboard uses a powerful module system powered by nwidart/laravel-modules. Modules are self-contained packages that extend LaraDashboard's functionality without modifying core code.

Overview

What Are Modules?

Modules are independent packages that:

  • Have their own MVC structure
  • Include migrations, seeders, and config
  • Register routes, views, and assets
  • Can be installed, activated, and removed
  • Are version-controlled independently

Benefits of Modular Architecture

Benefit Description
Separation of Concerns Each feature lives in its own module
Independent Updates Update modules without touching core
Easy Distribution Share modules as ZIP files or via marketplace
Clean Codebase Core remains lightweight and focused
Team Collaboration Different teams can work on different modules

Module Lifecycle

┌──────────────────────────────────────────────────────────────────┐
│                        Module Lifecycle                           │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌──────────┐     ┌──────────┐     ┌──────────┐                │
│   │ Uploaded │────▶│Installed │────▶│ Activated│                │
│   └──────────┘     └──────────┘     └────┬─────┘                │
│                                          │                       │
│                                          ▼                       │
│                    ┌─────────────────────────────────────┐      │
│                    │          Running in App             │       │
│                    │  • Service providers booted         │       │
│                    │  • Routes registered                │       │
│                    │  • Views available                  │       │
│                    │  • Migrations applied               │       │
│                    └─────────────────────────────────────┘      │
│                                          │                       │
│                    ┌─────────────────────┼─────────────────────┐│
│                    ▼                     ▼                     ▼│
│              ┌──────────┐         ┌───────────┐        ┌───────┐│
│              │Deactivated│        │  Updated  │        │Deleted││
│              └──────────┘         └───────────┘        └───────┘│
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Module Structure

Every module follows a standard structure:

modules/YourModule/
├── app/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── YourModuleController.php
│   │   ├── Middleware/
│   │   └── Requests/
│   ├── Livewire/
│   │   └── Admin/
│   │       └── Dashboard.php
│   ├── Models/
│   │   └── YourModel.php
│   ├── Providers/
│   │   ├── YourModuleServiceProvider.php
│   │   ├── RouteServiceProvider.php
│   │   └── EventServiceProvider.php
│   └── Services/
│       └── YourModuleService.php
│
├── config/
│   └── config.php
│
├── database/
│   ├── migrations/
│   │   └── 2024_01_01_000000_create_your_table.php
│   ├── seeders/
│   │   └── YourModuleDatabaseSeeder.php
│   └── factories/
│       └── YourModelFactory.php
│
├── resources/
│   ├── assets/
│   │   ├── css/
│   │   └── js/
│   └── views/
│       ├── admin/
│       │   └── index.blade.php
│       └── livewire/
│           └── admin/
│               └── dashboard.blade.php
│
├── routes/
│   ├── web.php
│   └── api.php
│
├── tests/
│   ├── Feature/
│   └── Unit/
│
├── composer.json
├── module.json
├── package.json (optional)
└── README.md

Configuration Files

module.json

The module manifest file:

{
    "name": "YourModule",
    "alias": "yourmodule",
    "description": "Description of your module",
    "keywords": ["feature", "extension"],
    "priority": 0,
    "providers": [
        "Modules\\YourModule\\App\\Providers\\YourModuleServiceProvider"
    ],
    "files": [],
    "requires": []
}
Field Description
name Module display name
alias Lowercase identifier
description Short description
keywords Searchable keywords
priority Load order (higher = earlier)
providers Service providers to register
files Files to autoload
requires Module dependencies

composer.json

Module-specific dependencies:

{
    "name": "laradashboard/yourmodule",
    "description": "Your module description",
    "type": "laradashboard-module",
    "require": {
        "php": "^8.3"
    },
    "autoload": {
        "psr-4": {
            "Modules\\YourModule\\": ""
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Modules\\YourModule\\App\\Providers\\YourModuleServiceProvider"
            ]
        }
    }
}

config/config.php

Module configuration:

<?php

return [
    'name' => 'YourModule',

    // Module-specific settings
    'settings' => [
        'feature_enabled' => env('YOURMODULE_FEATURE_ENABLED', true),
        'max_items' => env('YOURMODULE_MAX_ITEMS', 100),
    ],

    // Route configuration
    'routes' => [
        'prefix' => 'yourmodule',
        'middleware' => ['web', 'auth'],
    ],
];

Service Providers

Main Service Provider

<?php

namespace Modules\YourModule\App\Providers;

use Illuminate\Support\ServiceProvider;

class YourModuleServiceProvider extends ServiceProvider
{
    protected string $moduleName = 'YourModule';
    protected string $moduleNameLower = 'yourmodule';

    public function boot(): void
    {
        $this->registerTranslations();
        $this->registerConfig();
        $this->registerViews();
        $this->loadMigrationsFrom(
            module_path($this->moduleName, 'database/migrations')
        );
    }

    public function register(): void
    {
        $this->app->register(RouteServiceProvider::class);
        $this->app->register(EventServiceProvider::class);

        // Register module services
        $this->app->singleton(YourModuleService::class, function ($app) {
            return new YourModuleService();
        });
    }

    protected function registerConfig(): void
    {
        $this->publishes([
            module_path($this->moduleName, 'config/config.php')
                => config_path($this->moduleNameLower . '.php'),
        ], 'config');

        $this->mergeConfigFrom(
            module_path($this->moduleName, 'config/config.php'),
            $this->moduleNameLower
        );
    }

    protected function registerViews(): void
    {
        $viewPath = resource_path('views/modules/' . $this->moduleNameLower);
        $sourcePath = module_path($this->moduleName, 'resources/views');

        $this->publishes([
            $sourcePath => $viewPath
        ], ['views', $this->moduleNameLower . '-module-views']);

        $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [
            $sourcePath
        ]), $this->moduleNameLower);
    }

    protected function registerTranslations(): void
    {
        $langPath = resource_path('lang/modules/' . $this->moduleNameLower);

        if (is_dir($langPath)) {
            $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
        } else {
            $this->loadTranslationsFrom(
                module_path($this->moduleName, 'resources/lang'),
                $this->moduleNameLower
            );
        }
    }

    private function getPublishableViewPaths(): array
    {
        $paths = [];
        foreach (config('view.paths') as $path) {
            if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
                $paths[] = $path . '/modules/' . $this->moduleNameLower;
            }
        }
        return $paths;
    }
}

Route Service Provider

<?php

namespace Modules\YourModule\App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    protected string $moduleNamespace = 'Modules\YourModule\App\Http\Controllers';

    public function boot(): void
    {
        parent::boot();
    }

    public function map(): void
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

    protected function mapWebRoutes(): void
    {
        Route::middleware('web')
            ->namespace($this->moduleNamespace)
            ->group(module_path('YourModule', '/routes/web.php'));
    }

    protected function mapApiRoutes(): void
    {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->moduleNamespace)
            ->group(module_path('YourModule', '/routes/api.php'));
    }
}

Module Loading

Automatic Discovery

LaraDashboard automatically discovers modules in the modules/ directory:

// config/modules.php
return [
    'paths' => [
        base_path('Modules'),
        base_path('modules'),
    ],

    'auto_discover' => true,
];

Load Order

Modules are loaded based on:

  1. Priority - Higher priority loads first
  2. Dependencies - Required modules load before dependents
  3. Alphabetical - When priority is equal
// module.json
{
    "priority": 100,  // Load early
    "requires": ["AnotherModule"]  // Load after AnotherModule
}

Conditional Loading

Load modules conditionally:

// In YourModuleServiceProvider
public function register(): void
{
    // Only load if feature flag is enabled
    if (!config('yourmodule.enabled', true)) {
        return;
    }

    // Continue registration...
}

Database Management

Migrations

Create migrations for your module:

php artisan module:make-migration create_your_table YourModule

Migration file:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('yourmodule_items', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('name');
            $table->text('description')->nullable();
            $table->boolean('is_active')->default(true);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('yourmodule_items');
    }
};

Run module migrations:

# All modules
php artisan module:migrate

# Specific module
php artisan module:migrate YourModule

# Rollback
php artisan module:migrate-rollback YourModule

Seeders

<?php

namespace Modules\YourModule\Database\Seeders;

use Illuminate\Database\Seeder;

class YourModuleDatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Seed permissions
        $this->call(PermissionsSeeder::class);

        // Seed default data
        $this->call(DefaultDataSeeder::class);
    }
}

Run seeders:

php artisan module:seed YourModule

Module Communication

Using Hooks

Modules communicate via the hooks system:

// Module A: Register a hook
add_action('post.created', function (Post $post) {
    // Handle post creation
    YourModuleService::handlePostCreated($post);
});

// Module A: Register a filter
add_filter('post.content', function (string $content, Post $post) {
    // Modify content
    return $this->processContent($content);
}, 10, 2);

Using Events

// Module A: Dispatch event
event(new YourModuleEvent($data));

// Module B: Listen for event
// In EventServiceProvider
protected $listen = [
    \Modules\ModuleA\Events\YourModuleEvent::class => [
        \Modules\ModuleB\Listeners\HandleYourModuleEvent::class,
    ],
];

Using Service Container

// Module A: Bind service
$this->app->bind(YourContractInterface::class, YourImplementation::class);

// Module B: Resolve service
$service = app(YourContractInterface::class);

Module Management

Via Admin Panel

  1. Navigate to SettingsModules
  2. Upload module ZIP file or browse marketplace
  3. Click Install
  4. Activate the module
  5. Run migrations if prompted

Via Artisan Commands

# List all modules
php artisan module:list

# Enable module
php artisan module:enable YourModule

# Disable module
php artisan module:disable YourModule

# Create new module
php artisan module:make YourModule

# Generate module components
php artisan module:make-controller YourController YourModule
php artisan module:make-model YourModel YourModule
php artisan module:make-migration create_table YourModule
php artisan module:make-seeder YourSeeder YourModule
php artisan module:make-request YourRequest YourModule
php artisan module:make-policy YourPolicy YourModule

Module API

ModuleService

use App\Services\Modules\ModuleService;

// Get module service
$moduleService = app(ModuleService::class);

// Check if module is enabled
$isEnabled = $moduleService->isEnabled('YourModule');

// Get module info
$module = $moduleService->find('YourModule');

// Get all enabled modules
$enabled = $moduleService->getEnabled();

// Install module from path
$moduleService->install('/path/to/module.zip');

// Enable/disable
$moduleService->enable('YourModule');
$moduleService->disable('YourModule');

// Delete module
$moduleService->delete('YourModule');

Module Helpers

// Get module path
$path = module_path('YourModule', 'resources/views');

// Get module asset URL
$url = module_asset('YourModule', 'css/style.css');

// Check module status
if (Module::isEnabled('YourModule')) {
    // Module is active
}

// Get module config
$value = config('yourmodule.setting_key');

Best Practices

Naming Conventions

Type Convention Example
Module name PascalCase YourModule
Alias lowercase yourmodule
Tables prefix_table yourmodule_items
Config lowercase yourmodule.php
Views namespace lowercase yourmodule::view

Dependency Management

// module.json
{
    "requires": [
        "CoreModule",
        "AnotherModule"
    ]
}

Check dependencies in service provider:

public function boot(): void
{
    if (!Module::isEnabled('RequiredModule')) {
        throw new ModuleDependencyException(
            'YourModule requires RequiredModule to be enabled.'
        );
    }
}

Permissions

Register module-specific permissions:

// In migration or seeder
Permission::create(['name' => 'yourmodule.view', 'group' => 'yourmodule']);
Permission::create(['name' => 'yourmodule.create', 'group' => 'yourmodule']);
Permission::create(['name' => 'yourmodule.edit', 'group' => 'yourmodule']);
Permission::create(['name' => 'yourmodule.delete', 'group' => 'yourmodule']);

Configuration

Allow users to configure modules:

// Publish config
php artisan vendor:publish --tag=yourmodule-config

// Access config
$value = config('yourmodule.feature_enabled');

Troubleshooting

Module Not Loading

  1. Check module.json syntax
  2. Verify service provider namespace
  3. Clear caches:
    php artisan module:clear-compiled
    php artisan config:clear
    php artisan cache:clear
    

Migration Issues

# Reset and re-run migrations
php artisan module:migrate-refresh YourModule

# Check migration status
php artisan module:migrate-status YourModule

View Not Found

# Clear view cache
php artisan view:clear

# Publish module views
php artisan vendor:publish --tag=yourmodule-module-views

Next Steps

/