In my day job, I maintain a fairly large-ish Laravel application. It started out as a few separate vanilla PHP web apps, which I consolidated onto Laravel 4, then upgraded to Laravel 5. I’ve added a few new apps to the suite as business needs arose.
In the Laravel 4 version, I used creolab/laravel-modules to, well, modularize, the various apps under one Laravel instance. It worked well with a few gotchas, but the package wasn’t updated for Laravel 5. At that point I looked at a couple other module packages, but in the end decided it was simple enough to add modules to Laravel.
Assume our namespace is “CorpName”.
Add your namespace to composer.json
...
"psr-4": {
"App\\": "app/",
"CorpName\\": "app/CorpName/"
}
...
Create a directory structure like the following:
Add your Service Provider to the ‘providers’ array in config/app.php
... 'CorpName\Core\Providers\CorpNameServiceProvider', ...
Create “app/CorpName/Core/Providers/CorpNameServiceProvider.php”:
<?php
namespace CorpName\Core\Providers;
use Illuminate\Support\ServiceProvider;
use CorpName\Core\Bootstrap;
/**
* Load the CorpName Bootstrap helper, which in turn loads the modules.
* Wired up in app/config/app.php providers array
*/
class CorpNameServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$this->app['CorpName']->boot();
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
$this->app->singleton('CorpName', function($app)
{
return new Bootstrap($app);
});
}
}
Create “app/CorpName/Core/Bootstrap.php”:
<?php
namespace CorpName\Core;
use Illuminate\Foundation\Application;
/**
* Bootstrap the modules. Called from the ServiceProvider
*/
class Bootstrap
{
/**
* IoC
* @var \Illuminate\Foundation\Application
*/
protected $app;
public function __construct(Application $app)
{
$this->app = $app;
}
public function boot()
{
$this->loadModules();
}
/**
* Scan the CorpName/Modules directory for Modules, and load them (routes, views, includes)
*/
public function loadModules()
{
/**
* @var \Illuminate\Contracts\Filesystem\Filesystem $disk
*/
$disk = \Storage::disk('modules');
$modules = $disk->directories();
$modules_path = config('filesystems.disks.modules.root');
foreach ($modules as $module)
{
$module_path = $modules_path . '/' . $module;
//register routes
if($disk->exists("{$module}/routes.php"))
{
include $module_path . '/routes.php';
}
//set up views
if($disk->exists("{$module}/views"))
{
$this->app['view']->addNamespace($module, $module_path . '/views');
}
//include files
if($disk->exists("{$module}/includes"))
{
$includes = $disk->files("{$module}/includes");
foreach ($includes as $include)
{
if(substr($include, -4) == '.php')
{
include_once $modules_path . '/' . $include;
}
}
}
}
}
}
Note that I’m using a “modules” storage “disk” configured in “config/filesystem.php”:
... 'modules' => [ 'driver' => 'local', 'root' => app_path().'/CorpName/Modules', ], ...
And…
That’s basically it. You’ll probably want a routes.php file in each module, and a views directory, but you can structure your Modules as you see fit. If you use migrations, you’ll need to add your module/migrations directory to the autoload classmap in composer.json:
...
"autoload": {
"classmap": [
"database",
"app/CorpName/Modules/ModuleOne/migrations",
"app/CorpName/Modules/ModuleTwo/migrations",
"app/CorpName/Modules/ZeeAppModule/migrations"
],
...
Also, views can be referenced by module name in your controller and blade templates:
Controller:
...
return view('ModuleOne::index', $this->data);
...
Blade:
...
@extends('ModuleOne::layouts.master')
@section('apphead')
@include('ModuleTwo::partials.head')
@stop
...
This setup has been running in Laravel 5.0 and 5.1 for a year or so without problems. It could probably use some improvements, like caching, but it works as is.



