nwidart/laravel-modules
Modularize large Laravel apps with nwidart/laravel-modules. Create self-contained modules (controllers, models, views, routes, config) with Artisan generators, module discovery, enabling/disabling, and per-module resources—tested and maintained across modern Laravel versions.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require nwidart/laravel-modules
Publish config (optional):
php artisan vendor:publish --provider="Nwidart\Modules\LaravelModulesServiceProvider"
Enable Autoloading:
Update composer.json:
"extra": {
"merge-plugin": {
"include": ["Modules/*/composer.json"]
}
}
Allow plugins (answer y to prompt or manually add):
"config": {
"allow-plugins": {
"wikimedia/composer-merge-plugin": true
}
}
Run:
composer dump-autoload
First Module: Create a module skeleton:
php artisan module:make Blog --namespace="App\\Modules\\Blog"
Navigate to modules/Blog/ and explore:
src/Http/Controllers/ (Controllers)resources/views/ (Views)routes/web.php (Routes)src/Database/Migrations/ (Migrations)Enable/Disable:
php artisan module:enable Blog
php artisan module:disable Blog
Create a Module:
php artisan module:make [name] --namespace="App\\Modules\\[Name]"
Example: php artisan module:make Admin --namespace="App\\Modules\\Admin"
Generate Assets:
Use Laravel Mix/Vite within modules. Example resources/js/app.js in modules/Admin/:
import './bootstrap';
require('./admin');
Route Management:
Define routes in modules/[Name]/routes/web.php or routes/api.php.
Use RouteServiceProvider (auto-generated) for centralized route registration.
Database Migrations: Run migrations for a module:
php artisan module:migrate Blog
Rollback:
php artisan module:rollback Blog
Seeding: Seed module data:
php artisan module:seed Blog --class=BlogDatabaseSeeder
Service Providers:
Modules auto-register their ModuleServiceProvider. Extend core providers by binding interfaces in AppServiceProvider:
$this->app->bind(
\App\Contracts\BlogService::class,
\App\Modules\Blog\Services\BlogService::class
);
Views/Blades:
Use @include('modules::blog.partial') for shared views. Override module views by placing them in resources/views/vendor/modules/blog/.
Middleware:
Register module-specific middleware in ModuleServiceProvider:
protected function boot()
{
$this->app['router']->aliasMiddleware('blog.auth', \App\Modules\Blog\Http\Middleware\Authenticate::class);
}
Events/Listeners: Dispatch events from modules:
event(new \App\Modules\Blog\Events\PostPublished($post));
Listen in EventServiceProvider:
protected $listen = [
\App\Modules\Blog\Events\PostPublished::class => [
\App\Modules\Blog\Listeners\NotifyAdmins::class,
],
];
ModuleTestCase:
use Nwidart\Modules\Tests\ModuleTestCase;
class BlogTest extends ModuleTestCase
{
protected function getModule()
{
return 'Blog';
}
}
$this->disableModule('Blog');
Vite/Laravel Mix:
Configure vite.config.js in module root:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/js/admin.js'],
refresh: true,
}),
],
});
Build assets:
npm run dev --prefix modules/Admin
Shared Assets:
Use @vite(['resources/js/app.js', 'modules/Admin/resources/js/admin.js']) in resources/views/layouts/app.blade.php.
API Controllers: Generate API-specific controllers:
php artisan module:make-controller Admin/Api/PostController --api
Define routes in modules/Admin/routes/api.php:
Route::apiResource('posts', \App\Modules\Admin\Http\Controllers\Api\PostController::class);
Invokable Actions: Create lightweight actions:
php artisan module:make-action Admin/PublishPost
FileActivator (database-backed) for dynamic enabling/disabling:
php artisan module:v6:migrate # Migrate to FileActivator
Toggle via:
\Modules::enable('Blog');
\Modules::disable('Blog');
Service Binding:
Bind module services in AppServiceProvider:
$this->app->when(\App\Modules\Blog\Services\BlogService::class)
->needs(\App\Modules\Admin\Services\AdminService::class)
->give(\App\Modules\Admin\Services\AdminService::class);
Shared Config: Publish module configs:
php artisan vendor:publish --tag="blog-config" --provider="App\Modules\Blog\Providers\BlogServiceProvider"
Extend Artisan Commands:
Create a custom command in app/Console/Commands:
use Illuminate\Console\GeneratorCommand;
class MakeModuleModel extends GeneratorCommand
{
protected $type = 'Module Model';
protected function getStub() { /* ... */ }
protected function getDefaultNamespace() { /* ... */ }
}
Register in app/Console/Kernel.php:
protected $commands = [
\App\Console\Commands\MakeModuleModel::class,
];
Custom Stubs: Publish stubs:
php artisan vendor:publish --tag="stubs"
Override in stubs/ directory.
Composer Autoloading:
Ensure module packages are autoloaded via merge-plugin:
"extra": {
"merge-plugin": {
"include": [
"Modules/*/composer.json",
"vendor/*/composer.json"
]
}
}
Package Development:
Structure packages within modules (e.g., modules/Blog/vendor/app/blog-package).
Autoloading Issues:
Class not found errors for module classes.merge-plugin is enabled in composer.json.composer dump-autoload after adding modules.allow-plugins is configured:
"config": {
"allow-plugins": {
"wikimedia/composer-merge-plugin": true
}
}
Route Conflicts:
/admin).RouteServiceProvider:
Route::module('admin', 'App\Modules\Admin\Http\Controllers')
->prefix('admin')
->group(base_path('modules/Admin/routes/web.php'));
Migration Failures:
Table already exists when running module:migrate.--force to drop tables:
php artisan module:migrate Blog --force
php artisan module:rollback Blog
Asset Compilation Errors:
vite.config.js exists in the module root.@vite(['resources/js/app.js', 'modules/Blog/resources/js/blog.js']) in Blade.public/build/ permissions.5
How can I help you explore Laravel packages today?