# Laravel Modular Laravel Modular is a powerful modular architecture package for Laravel applications that enables developers to organize their codebase into independent, reusable modules with automatic discovery and zero configuration. The package leverages Composer's merge plugin to provide native autoloading, eliminating the need for manual module registration while maintaining optimal performance through built-in caching and lazy loading mechanisms. This package is designed for Laravel 11 & 12 applications running PHP 8.2+, providing a complete module structure including controllers, models, views, routes, migrations, and translations. It features debug-aware logging that respects the APP_DEBUG environment variable, comprehensive Artisan commands for module management, and performance monitoring capabilities. With 23.1% improved peak memory usage compared to alternatives like nWidart/laravel-modules, it offers a lightweight, composer-first approach to modular application architecture. ## Installation Install the package via Composer and configure the merge plugin for automatic module discovery. ```bash composer require saeedvir/laravel-modular ``` ```json { "extra": { "merge-plugin": { "include": ["modules/*/composer.json"] } }, "config": { "allow-plugins": { "wikimedia/composer-merge-plugin": true } } } ``` ```bash # Optional: publish configuration file php artisan vendor:publish --tag=module-config ``` ## Creating a Module Create a new module with complete directory structure, service provider, and configuration. ```bash # Create a new Blog module php artisan module:make Blog # Refresh autoloading to register the module composer dump-autoload ``` ``` # Generated structure: modules/Blog/ ├── app/ │ ├── Console/ │ ├── Http/ │ │ ├── Controllers/ │ │ │ └── BlogController.php │ │ ├── Middleware/ │ │ └── Requests/ │ ├── Models/ │ ├── Providers/ │ │ └── BlogServiceProvider.php │ └── Services/ ├── config/ │ └── config.php ├── database/ │ ├── migrations/ │ ├── seeders/ │ └── factories/ ├── resources/ │ ├── views/ │ └── lang/ ├── routes/ │ ├── web.php │ └── api.php └── composer.json ``` ## Listing All Modules Display all discovered modules with their status (enabled/disabled) and metadata. ```bash php artisan module:list ``` ``` # Output example: +--------+---------+--------------------------------------------------+ | Name | Status | Path | +--------+---------+--------------------------------------------------+ | Blog | Enabled | /var/www/html/modules/Blog | | Shop | Enabled | /var/www/html/modules/Shop | | Admin | Enabled | /var/www/html/modules/Admin | +--------+---------+--------------------------------------------------+ ``` ## Creating a Module Controller Generate a controller within a specific module with support for API and resource patterns. ```bash # Create a standard controller php artisan module:make-controller Blog PostController # Create an API controller php artisan module:make-controller Blog PostController --api # Create a resource controller with all CRUD methods php artisan module:make-controller Blog PostController --resource ``` ```php paginate(10); return view('blog::posts.index', compact('posts')); } public function store(Request $request) { $validated = $request->validate([ 'title' => 'required|string|max:255', 'content' => 'required|string', 'slug' => 'required|string|unique:posts' ]); $post = Post::create($validated); return redirect()->route('blog.posts.show', $post->id); } } ``` ## Creating Module Migrations Generate database migrations within a module's migration directory. ```bash # Create a migration for the Blog module php artisan module:make-migration Blog create_posts_table # Run all migrations (includes module migrations) php artisan migrate ``` ```php id(); $table->string('title'); $table->string('slug')->unique(); $table->text('content'); $table->timestamp('published_at')->nullable(); $table->timestamps(); }); } public function down(): void { Schema::dropIfExists('posts'); } }; ``` ## Creating Form Requests Generate form request classes for validation within modules. ```bash php artisan module:make-request Blog StorePostRequest ``` ```php 'required|string|max:255', 'content' => 'required|string|min:50', 'slug' => 'required|string|unique:posts,slug', 'published_at' => 'nullable|date' ]; } } ``` ## Creating API Resources Generate API resource transformers for consistent JSON responses. ```bash php artisan module:make-resource Blog PostResource ``` ```php $this->id, 'title' => $this->title, 'slug' => $this->slug, 'content' => $this->content, 'published_at' => $this->published_at?->toISOString(), 'created_at' => $this->created_at->toISOString(), 'updated_at' => $this->updated_at->toISOString(), ]; } } ``` ## Defining Module Routes Configure web and API routes with automatic module prefixing. ```php name('index'); Route::resource('posts', PostController::class); // Routes accessible at: // GET /blog/ -> blog.index // GET /blog/posts -> blog.posts.index // POST /blog/posts -> blog.posts.store // GET /blog/posts/{id} -> blog.posts.show ``` ```php group(function () { Route::post('/posts', [PostApiController::class, 'store']); Route::put('/posts/{id}', [PostApiController::class, 'update']); Route::delete('/posts/{id}', [PostApiController::class, 'destroy']); }); // Routes accessible at: // GET /api/blog/posts // GET /api/blog/posts/{id} // POST /api/blog/posts (authenticated) ``` ## Loading Module Views Reference views using the module namespace convention. ```php paginate(10); // Load view from modules/Blog/resources/views/posts/index.blade.php return view('blog::posts.index', compact('posts')); } public function show($id) { $post = Post::findOrFail($id); // Load view from modules/Blog/resources/views/posts/show.blade.php return view('blog::posts.show', compact('post')); } } ``` ## Module Configuration Define and access module-specific configuration values. ```php 'Blog', 'enabled' => true, 'version' => '1.0.0', 'posts_per_page' => 15, 'allow_comments' => true, 'cache_duration' => 3600, ]; ``` ```php 'Blog', 'path' => '...', 'enabled' => true, 'provider' => '...'] // Get module path $modulesPath = Module::getPath(); // /var/www/html/modules $blogPath = Module::getPath('Blog'); // /var/www/html/modules/Blog // Create module programmatically Module::create('Shop'); // Delete module programmatically Module::delete('Shop'); // Clear module cache Module::clearCache(); // Get performance metrics $metrics = Module::getPerformanceMetrics(); ``` ## Disabling Modules Disable specific modules without deleting them from the filesystem. ```php base_path('modules'), 'disabled' => [ 'OldBlog', 'LegacyShop', 'DeprecatedAdmin', ], 'cache' => [ 'enabled' => env('MODULE_CACHE_ENABLED', true), 'key' => 'laravel_modular_cache', 'lifetime' => 86400, ], 'auto_register_routes' => true, 'stubs_path' => null, ]; ``` ## Caching Module Discovery Optimize performance by caching module discovery results. ```bash # Cache module discovery for production php artisan module:cache # Clear module cache php artisan cache:clear ``` ```php $this->faker->sentence(), 'slug' => $this->faker->slug(), 'content' => $this->faker->paragraphs(5, true), 'published_at' => $this->faker->dateTimeBetween('-1 year', 'now'), ]; } } ``` ## Creating Module Seeders Generate database seeders for populating module data. ```bash php artisan module:make-seeder Blog PostSeeder ``` ```php count(50)->create(); // Create featured posts Post::factory()->count(5)->create([ 'published_at' => now(), 'featured' => true, ]); } } ``` ## Running Module Tests Execute tests specific to a module. ```bash # Run tests for a specific module php artisan module:test Blog # Run all tests including module tests php artisan test ``` ```php count(5)->create(); $response = $this->get('/blog/posts'); $response->assertStatus(200); $response->assertViewHas('posts'); } public function test_can_create_post(): void { $data = [ 'title' => 'Test Post', 'slug' => 'test-post', 'content' => 'This is test content for the post.', ]; $response = $this->post('/blog/posts', $data); $response->assertRedirect(); $this->assertDatabaseHas('posts', ['slug' => 'test-post']); } } ``` ## Removing a Module Delete a module and all its files from the filesystem. ```bash # Remove a module completely php artisan module:remove Blog # Confirm deletion when prompted # This will delete the entire modules/Blog directory # Refresh autoloading after removal composer dump-autoload ``` ## Module Service Provider Customize module bootstrapping, route registration, and resource publishing. ```php mergeConfigFrom( __DIR__.'/../../config/config.php', 'blog' ); // Register services $this->app->singleton(BlogService::class); } public function boot(): void { // Load migrations $this->loadMigrationsFrom(__DIR__.'/../../database/migrations'); // Load views $this->loadViewsFrom(__DIR__.'/../../resources/views', 'blog'); // Load translations $this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'blog'); // Register routes $this->registerRoutes(); // Register Livewire components $this->registerLivewireComponents(); // Publish resources if ($this->app->runningInConsole()) { $this->publishes([ __DIR__.'/../../config/config.php' => config_path('blog.php'), ], 'blog-config'); $this->publishes([ __DIR__.'/../../resources/views' => resource_path('views/vendor/blog'), ], 'blog-views'); } } protected function registerRoutes(): void { Route::group([ 'middleware' => 'web', 'prefix' => 'blog', 'namespace' => 'Modules\Blog\Http\Controllers', ], function () { $this->loadRoutesFrom(__DIR__.'/../../routes/web.php'); }); Route::group([ 'middleware' => 'api', 'prefix' => 'api/blog', 'namespace' => 'Modules\Blog\Http\Controllers\Api', ], function () { $this->loadRoutesFrom(__DIR__.'/../../routes/api.php'); }); } protected function registerLivewireComponents(): void { Livewire::component('blog::post-list', \Modules\Blog\Livewire\PostList::class); Livewire::component('blog::comment-form', \Modules\Blog\Livewire\CommentForm::class); } } ``` ## Creating Module Models Define Eloquent models within module namespaces with proper relationships. ```php 'datetime', 'featured' => 'boolean', ]; protected static function newFactory() { return PostFactory::new(); } public function comments() { return $this->hasMany(Comment::class); } public function scopePublished($query) { return $query->whereNotNull('published_at') ->where('published_at', '<=', now()); } } ``` Laravel Modular provides an elegant solution for building scalable Laravel applications through a modular architecture that emphasizes developer productivity and application maintainability. The primary use cases include organizing large monolithic applications into logical, self-contained modules; building multi-tenant applications where each tenant can have custom modules; creating reusable business logic packages that can be shared across multiple projects; and developing microservice-style architectures within a monolithic Laravel application. Teams can work independently on different modules without conflicts, and modules can be enabled or disabled without affecting the rest of the application. The package integrates seamlessly with Laravel's existing ecosystem, requiring no changes to standard Laravel development workflows. All Artisan commands, routing, migrations, views, and configuration work exactly as they do in traditional Laravel applications, but organized within module boundaries. The composer-first approach means that module autoloading is handled natively by Composer's PSR-4 specification, eliminating custom autoloader overhead and ensuring optimal performance. With built-in caching, debug-aware logging, and performance monitoring, Laravel Modular is production-ready and designed for high-traffic applications where performance and maintainability are critical requirements.