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

v0.0.1 2026-01-27 11:31 UTC

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() or routes()),
  • 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 () => ...) inside register(). 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 extends Illuminate\Console\Command is registered.

  • Migrations: app/<module>/migrations is loaded when running in console.

  • Translations: app/<module>/lang is 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.phpview('<module>::reports.index')).

Defining routes

You have two options:

  1. 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');
    }
}
  1. routes() closure (useful if you want to keep all routing inside register())
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 name
  • patterns = *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 name traffic.
  • The command scanner builds class names based on your app namespace (App\) and file paths.