prometa / laravel-modules
Helpers for bootstrapping modular Laravel apps (routes, commands, migrations, and service providers).
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/prometa/laravel-modules
Requires
- php: ^8.2
- laravel/framework: >=12.0
Suggests
- spatie/laravel-route-attributes: Enable attribute-based route discovery for modules
This package is auto-updated.
Last update: 2026-01-27 12:07:41 UTC
README
Helpers for bootstrapping modular Laravel apps. It provides a single trait you can mix into your module ServiceProviders to:
- auto-load module routes (via
map()orroutes()), - auto-register module commands (scan the module folder),
- auto-register module migrations and translations,
Requirements
- PHP ^8.2
- Laravel >=12 (see
composer.json)
Installation
composer require prometa/laravel-modules
Usage
Use the trait in each module’s ServiceProvider:
<?php namespace App\traffic; use Illuminate\Support\Facades\Route; use Prometa\BootstrapModules\BootstrapsModule; class ServiceProvider extends \Illuminate\Support\ServiceProvider { use BootstrapsModule; public function map(): void { Route::middleware('web') ->group(__DIR__ . '/routes.php'); } }
When you have your own register() or boot()
BootstrapsModule defines register() and boot(). If your ServiceProvider also defines
one of those methods, you need to alias the trait methods so you can call both:
class ServiceProvider extends \Illuminate\Support\ServiceProvider { use BootstrapsModule { register as registerModule; boot as bootModule; } public function register(): void { $this->registerModule(); // trait logic // your bindings... } public function boot(): void { $this->bootModule(); // trait logic // your boot logic... } }
If you only override register() (or only boot()), you only need to alias that one.
Module folder schema
The trait assumes one module per folder under app/ (snake_case recommended), and uses the module name from the ServiceProvider’s namespace (last segment).
Minimal example:
app/
traffic/
ServiceProvider.php
routes.php (optional)
Full example (optional parts):
app/
traffic/
ServiceProvider.php
routes.php
migrations/
2023_01_01_000000_create_traffic_table.php
lang/
en/
messages.php
Console/
SyncTraffic.php
reports/
index.blade.php
What happens automatically
-
Routes: if the ServiceProvider has a
map()method, it is called after boot.
Alternatively you can call$this->routes(fn () => ...)insideregister(). This is only needed if the Module has Routes to load. -
Commands: when running in console, the module directory is scanned.
Any non-abstract class that extendsIlluminate\Console\Commandis registered. -
Migrations:
app/<module>/migrationsis loaded when running in console. -
Translations:
app/<module>/langis loaded and namespaced as<module>. -
Views:
app/<module>is registered as a view namespace<module>(so any subfolders are allowed, e.g.app/<module>/reports/index.blade.php→view('<module>::reports.index')).
Defining routes
You have two options:
map()method (most common)
class ServiceProvider extends \Illuminate\Support\ServiceProvider { use BootstrapsModule; public function map(): void { Route::middleware('web') ->prefix('traffic') ->name('traffic::') ->group(__DIR__ . '/routes.php'); } }
routes()closure (useful if you want to keep all routing insideregister())
class ServiceProvider extends \Illuminate\Support\ServiceProvider { use BootstrapsModule { register as registerModule; } public function register(): void { $this->registerModule(); $this->routes(function () { Route::middleware('web') ->prefix('traffic') ->name('traffic::') ->group(__DIR__ . '/routes.php'); }); } }
If you do neither, the module simply has no routes.
Route attributes integration (optional)
If you use spatie/laravel-route-attributes, the trait automatically appends the module
root to route-attributes.directories with sane defaults:
prefix= module namepatterns=*Controller.php- no middleware
This keeps routes concise while allowing full control via attributes.
The integration is opt-in by presence: we only enable it if
Spatie\RouteAttributes\RouteAttributesServiceProvider exists.
Customize or disable
You can override the config per module by implementing this method in your ServiceProvider:
protected function routeAttributesDirectoryConfig(): array { return [ 'prefix' => $this->moduleName(), 'patterns' => ['*Controller.php'], // 'middleware' => ['web'], ]; }
To disable the auto-registration for a module, override:
protected function registerRouteAttributesDirectory(): void { // no-op }
Notes
- The module name is inferred from the ServiceProvider’s namespace, e.g.
App\traffic\ServiceProvider→ module nametraffic. - The command scanner builds class names based on your app namespace (
App\) and file paths.