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:
- Priority - Higher priority loads first
- Dependencies - Required modules load before dependents
- 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
- Navigate to Settings → Modules
- Upload module ZIP file or browse marketplace
- Click Install
- Activate the module
- 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
- Check
module.jsonsyntax - Verify service provider namespace
- 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
- Module Development Guide - Build your own module
- Hooks System - Extend functionality
- Architecture Overview - System design