├── tests └── .gitkeep ├── src ├── config │ ├── .gitkeep │ └── config.php ├── Commands │ ├── ModulesCommand.php │ ├── ModulesScanCommand.php │ ├── ModulesSeedCommand.php │ ├── ModulesGenerateCommand.php │ ├── AbstractCommand.php │ ├── ModulesCreateCommand.php │ ├── ModulesPublishCommand.php │ └── ModulesMigrateCommand.php ├── ModuleCollection.php ├── Manifest.php ├── ServiceProvider.php ├── Finder.php └── Module.php ├── .gitignore ├── .travis.yml ├── phpunit.xml ├── composer.json └── readme.md /tests/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/config/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - curl -s http://getcomposer.org/installer | php 10 | - php composer.phar install --dev 11 | 12 | script: phpunit -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "creolab/laravel-modules", 3 | "keywords": ["modules", "laravel"], 4 | "description": "Application specific modules in Laravel 4", 5 | "homepage": "http://creolab.hr", 6 | "authors": [ 7 | { 8 | "name": "Boris Strahija", 9 | "email": "bstrahija@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.3.0", 14 | "laravel/framework": "~4" 15 | }, 16 | "require-dev": { 17 | "way/generators": "~2" 18 | }, 19 | "suggest": { 20 | "way/generators": "Required for modules:generate functionality." 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Creolab\\LaravelModules\\": "src/" 25 | } 26 | }, 27 | "minimum-stability": "dev" 28 | } 29 | -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | 'app/modules', 10 | 11 | /** 12 | * If set to 'auto', the modules path will be scanned for modules 13 | */ 14 | 'mode' => 'auto', 15 | 16 | /** 17 | * In case the auto detect mode is disabled, these modules will be loaded 18 | * If the mode is set to 'auto', this setting will be discarded 19 | */ 20 | 'modules' => array( 21 | 'auth' => array('enabled' => true), 22 | 'content' => array('enabled' => false), 23 | 'shop' => array('enabled' => true), 24 | ), 25 | 26 | /** 27 | * Default files that are included automatically for each module 28 | */ 29 | 'include' => array( 30 | 'helpers.php', 31 | 'bindings.php', 32 | 'observers.php', 33 | 'filters.php', 34 | 'composers.php', 35 | 'routes.php', 36 | ), 37 | 38 | /** 39 | * Debug mode 40 | */ 41 | 'debug' => false, 42 | 43 | ); 44 | -------------------------------------------------------------------------------- /src/Commands/ModulesCommand.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ModulesCommand extends AbstractCommand { 13 | 14 | /** 15 | * Name of the command 16 | * @var string 17 | */ 18 | protected $name = 'modules'; 19 | 20 | /** 21 | * Command description 22 | * @var string 23 | */ 24 | protected $description = 'Just return all registered modules.'; 25 | 26 | /** 27 | * Execute the console command. 28 | * @return void 29 | */ 30 | public function fire() 31 | { 32 | // Return error if no modules found 33 | if (count($this->getModules()) == 0) 34 | { 35 | return $this->error("Your application doesn't have any registered modules."); 36 | } 37 | 38 | // Display the modules info 39 | $this->displayModules($this->getModules()); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/ModuleCollection.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ModuleCollection extends Collection { 11 | 12 | /** 13 | * List of all modules 14 | * @var array 15 | */ 16 | public $items = array(); 17 | 18 | /** 19 | * IoC 20 | * @var Illuminate\Foundation\Application 21 | */ 22 | protected $app; 23 | 24 | /** 25 | * Initialize a module collection 26 | * @param Application $app 27 | */ 28 | public function __construct(Application $app) 29 | { 30 | $this->app = $app; 31 | } 32 | 33 | /** 34 | * Initialize all modules 35 | * @return void 36 | */ 37 | public function registerModules() 38 | { 39 | // First we need to sort the modules 40 | $this->sort(function($a, $b) { 41 | if ($a->order == $b->order) return 0; 42 | else return $a->order < $b->order ? -1 : 1; 43 | }); 44 | 45 | // Then register each one 46 | foreach ($this->items as $module) 47 | { 48 | $module->register(); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/Commands/ModulesScanCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ModulesScanCommand extends AbstractCommand { 14 | 15 | /** 16 | * Name of the command 17 | * @var string 18 | */ 19 | protected $name = 'modules:scan'; 20 | 21 | /** 22 | * Command description 23 | * @var string 24 | */ 25 | protected $description = 'Scan modules and cache module meta data.'; 26 | 27 | /** 28 | * Path to the modules monifest 29 | * @var string 30 | */ 31 | protected $manifestPath; 32 | 33 | /** 34 | * Execute the console command. 35 | * @return void 36 | */ 37 | public function fire() 38 | { 39 | $this->info('Scanning modules'); 40 | 41 | // Get table helper 42 | $this->table = $this->getHelperSet()->get('table'); 43 | 44 | // Delete the manifest 45 | $this->app['modules']->deleteManifest(); 46 | 47 | // Run the scanner 48 | $this->modules = $this->app['modules']->scan(); 49 | 50 | // Return error if no modules found 51 | if (count($this->modules) == 0) 52 | { 53 | return $this->error("Your application doesn't have any valid modules."); 54 | } 55 | 56 | // Also run composer dump-autoload 57 | $this->dumpAutoload(); 58 | 59 | // Display number of found modules 60 | $this->info('Found ' . count($this->modules) . ' modules:'); 61 | 62 | // Display the modules info 63 | $this->displayModules($this->getModules()); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/Commands/ModulesSeedCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ModulesSeedCommand extends AbstractCommand { 14 | 15 | /** 16 | * Name of the command 17 | * @var string 18 | */ 19 | protected $name = 'modules:seed'; 20 | 21 | /** 22 | * Command description 23 | * @var string 24 | */ 25 | protected $description = 'Seed the database from modules.'; 26 | 27 | /** 28 | * Execute the console command. 29 | * @return void 30 | */ 31 | public function fire() 32 | { 33 | $this->info('Seeding database from modules'); 34 | 35 | // Get all modules or 1 specific 36 | if ($moduleName = $this->input->getArgument('module')) $modules = array(app('modules')->module($moduleName)); 37 | else $modules = app('modules')->modules(); 38 | 39 | foreach ($modules as $module) 40 | { 41 | if ($module) 42 | { 43 | if ($module->def('seeder')) 44 | { 45 | $module->seed(); 46 | $this->info("Seeded '" . $module->name() . "' module."); 47 | } 48 | else 49 | { 50 | $this->line("Module '" . $module->name() . "' has no seeds."); 51 | } 52 | } 53 | else 54 | { 55 | $this->error("Module '" . $moduleName . "' does not exist."); 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * Get the console command arguments. 62 | * @return array 63 | */ 64 | protected function getArguments() 65 | { 66 | return array( 67 | array('module', InputArgument::OPTIONAL, 'The name of module being seeded.'), 68 | ); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/Manifest.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Manifest { 10 | 11 | /** 12 | * Path to manifest file 13 | * @var string 14 | */ 15 | protected $path; 16 | 17 | /** 18 | * Manifest data 19 | * @var array 20 | */ 21 | protected $data; 22 | 23 | /** 24 | * IoC 25 | * @var Illuminate\Foundation\Application 26 | */ 27 | protected $app; 28 | 29 | /** 30 | * Initialize the manifest 31 | * @param Application $app [description] 32 | */ 33 | public function __construct(Application $app) 34 | { 35 | $this->app = $app; 36 | 37 | // Path to manifest file 38 | $this->path = storage_path('meta/modules.json'); 39 | 40 | // Try to read the file 41 | if ($this->app['files']->exists($this->path)) 42 | { 43 | $this->data = @json_decode($this->app['files']->get($this->path), true); 44 | } 45 | } 46 | 47 | /** 48 | * Save the manifest data 49 | * @return void 50 | */ 51 | public function save($modules) 52 | { 53 | // Prepare manifest data 54 | foreach ($modules as $module) 55 | { 56 | $this->data[$module->name()] = $module->def(); 57 | } 58 | 59 | // Cache it 60 | try 61 | { 62 | $this->app['files']->put($this->path, $this->app['modules']->prettyJsonEncode($this->data)); 63 | } 64 | catch(\Exception $e) 65 | { 66 | $this->app['log']->error("[MODULES] Failed when saving manifest file: " . $e->getMessage()); 67 | } 68 | 69 | return $this->data; 70 | } 71 | 72 | /** 73 | * Get the manifest data as an array 74 | * @return array 75 | */ 76 | public function toArray($module = null) 77 | { 78 | if ($module) return $this->data[$module]; 79 | else return $this->data; 80 | } 81 | 82 | /** 83 | * Delete the manifest file 84 | * @return void 85 | */ 86 | public function delete() 87 | { 88 | $this->data = null; 89 | 90 | $this->app['files']->delete($this->path); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Commands/ModulesGenerateCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ModulesGenerateCommand extends AbstractCommand { 14 | 15 | /** 16 | * Name of the command 17 | * @var string 18 | */ 19 | protected $name = 'modules:generate'; 20 | 21 | /** 22 | * Command description 23 | * @var string 24 | */ 25 | protected $description = 'Generate module resources.'; 26 | 27 | /** 28 | * Execute the console command. 29 | * @return void 30 | */ 31 | public function fire() 32 | { 33 | // Name of new module 34 | $module = $this->getModule($this->input->getArgument('module')); 35 | $modulePath = base_path(ltrim($module['path'], '/')); 36 | $type = $this->input->getArgument('type'); 37 | $resource = $this->input->getArgument('resource'); 38 | 39 | // Generate a controller 40 | if ($type == 'controller') 41 | { 42 | $dirPath = $modulePath . '/controllers'; 43 | $this->call('generate:controller', array('controllerName' => $resource, '--path' => $dirPath)); 44 | } 45 | 46 | // Generate a model 47 | if ($type == 'model') 48 | { 49 | $dirPath = $modulePath . '/models'; 50 | $this->call('generate:model', array('modelName' => $resource, '--path' => $dirPath)); 51 | } 52 | 53 | // Generate a migration 54 | if ($type == 'migration') 55 | { 56 | $dirPath = $modulePath . '/migrations'; 57 | $this->call('generate:migration', array('migrationName' => $resource, '--path' => $dirPath)); 58 | } 59 | 60 | // Generate a view 61 | if ($type == 'view') 62 | { 63 | $dirPath = $modulePath . '/views'; 64 | $this->call('generate:view', array('viewName' => $resource, '--path' => $dirPath)); 65 | } 66 | } 67 | 68 | /** 69 | * Get the console command arguments. 70 | * @return array 71 | */ 72 | protected function getArguments() 73 | { 74 | return array( 75 | array('module', InputArgument::REQUIRED, 'The name of module.'), 76 | array('type', InputArgument::REQUIRED, 'Type of resource you want to generate.'), 77 | array('resource', InputArgument::REQUIRED, 'Name of resource.'), 78 | ); 79 | } 80 | 81 | /** 82 | * Get the console command options. 83 | * @return array 84 | */ 85 | protected function getOptions() 86 | { 87 | return array( 88 | array('path', null, InputOption::VALUE_OPTIONAL, 'Path to the directory.'), 89 | array('template', null, InputOption::VALUE_OPTIONAL, 'Path to template.') 90 | ); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Commands/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | abstract class AbstractCommand extends Command { 14 | 15 | /** 16 | * List of all available modules 17 | * 18 | * @var array 19 | */ 20 | protected $modules; 21 | 22 | /** 23 | * IoC 24 | * 25 | * @var Illuminate\Foundation\Application 26 | */ 27 | protected $app; 28 | 29 | /** 30 | * DI 31 | * 32 | * @param Application $app 33 | */ 34 | public function __construct(Application $app) 35 | { 36 | parent::__construct(); 37 | $this->app = $app; 38 | } 39 | 40 | /** 41 | * Get the console command options. 42 | * 43 | * @return array 44 | */ 45 | protected function getOptions() 46 | { 47 | return array(); 48 | } 49 | 50 | /** 51 | * Reformats the modules list for table display 52 | * 53 | * @return array 54 | */ 55 | public function getModules() 56 | { 57 | $results = array(); 58 | 59 | foreach($this->app['modules']->modules() as $name => $module) 60 | { 61 | $path = str_replace(app()->make('path.base'), '', $module->path()); 62 | 63 | $results[] = array( 64 | 'name' => $name, 65 | 'path' => $path, 66 | 'order' => $module->order, 67 | 'enabled' => $module->enabled() ? 'true' : '', 68 | ); 69 | } 70 | 71 | return array_filter($results); 72 | } 73 | 74 | 75 | /** 76 | * Return a given module 77 | * 78 | * @param $module_name 79 | * @return mixed 80 | */ 81 | public function getModule($module_name) 82 | { 83 | foreach ($this->getModules() as $module) { 84 | if ($module['name'] == $module_name) { 85 | return $module; 86 | } 87 | } 88 | return false; 89 | } 90 | 91 | /** 92 | * Display a module info table in the console 93 | * 94 | * @param array $modules 95 | * @return void 96 | */ 97 | public function displayModules($modules) 98 | { 99 | // Get table helper 100 | $this->table = $this->getHelperSet()->get('table'); 101 | 102 | $headers = array('Name', 'Path', 'Order', 'Enabled'); 103 | 104 | $this->table->setHeaders($headers)->setRows($modules); 105 | 106 | $this->table->render($this->getOutput()); 107 | } 108 | 109 | /** 110 | * Dump autoload classes 111 | * 112 | * @return void 113 | */ 114 | public function dumpAutoload() 115 | { 116 | // Also run composer dump-autoload 117 | $composer = new Composer($this->app['files']); 118 | $this->info('Generating optimized class loader'); 119 | $composer->dumpOptimized(); 120 | $this->line(''); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Commands/ModulesCreateCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ModulesCreateCommand extends AbstractCommand { 14 | 15 | /** 16 | * Name of the command 17 | * @var string 18 | */ 19 | protected $name = 'modules:create'; 20 | 21 | /** 22 | * Command description 23 | * @var string 24 | */ 25 | protected $description = 'Create a new module.'; 26 | 27 | /** 28 | * Execute the console command. 29 | * @return void 30 | */ 31 | public function fire() 32 | { 33 | // Name of new module 34 | $moduleName = $this->input->getArgument('module'); 35 | $this->info('Creating module "'.$moduleName.'"'); 36 | 37 | // Chech if module exists 38 | if ( ! app('modules')->module($moduleName)) 39 | { 40 | // Get path to modules 41 | $modulePath = $this->app['config']->get('modules::path'); 42 | if (is_array($modulePath)) $modulePath = $modulePath[0]; 43 | $modulePath .= '/' . $moduleName; 44 | 45 | // Create the directory 46 | if ( ! $this->app['files']->exists($modulePath)) 47 | { 48 | $this->app['files']->makeDirectory($modulePath, 0755, true); 49 | } 50 | 51 | // Create definition and write to file 52 | $definition = $this->app['modules']->prettyJsonEncode(array('enabled' => true)); 53 | $this->app['files']->put($modulePath . '/module.json', $definition); 54 | 55 | // Create routes and write to file 56 | $routes = 'app['files']->put($modulePath . '/routes.php', $routes); 58 | 59 | // Create some resource directories 60 | $this->app['files']->makeDirectory($modulePath . '/assets', 0755); 61 | $this->app['files']->makeDirectory($modulePath . '/config', 0755); 62 | $this->app['files']->makeDirectory($modulePath . '/controllers', 0755); 63 | $this->app['files']->makeDirectory($modulePath . '/lang', 0755); 64 | $this->app['files']->makeDirectory($modulePath . '/models', 0755); 65 | $this->app['files']->makeDirectory($modulePath . '/migrations', 0755); 66 | $this->app['files']->makeDirectory($modulePath . '/views', 0755); 67 | 68 | // Autoload classes 69 | $this->dumpAutoload(); 70 | } 71 | else 72 | { 73 | $this->error('Module with name "'.$moduleName.'" already exists.'); 74 | } 75 | } 76 | 77 | /** 78 | * Get the console command arguments. 79 | * @return array 80 | */ 81 | protected function getArguments() 82 | { 83 | return array( 84 | array('module', InputArgument::REQUIRED, 'The name of module being created.'), 85 | ); 86 | } 87 | 88 | /** 89 | * Get the console command options. 90 | * @return array 91 | */ 92 | protected function getOptions() 93 | { 94 | return array(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/Commands/ModulesPublishCommand.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ModulesPublishCommand extends AbstractCommand { 14 | 15 | /** 16 | * Name of the command 17 | * @var string 18 | */ 19 | protected $name = 'modules:publish'; 20 | 21 | /** 22 | * Command description 23 | * @var string 24 | */ 25 | protected $description = 'Publish public assets for modules.'; 26 | 27 | /** 28 | * Execute the console command. 29 | * @return void 30 | */ 31 | public function fire() 32 | { 33 | $this->info('Publishing module assets'); 34 | 35 | // Get all modules or 1 specific 36 | if ($moduleName = $this->input->getArgument('module')) $modules = array(app('modules')->module($moduleName)); 37 | else $modules = app('modules')->modules(); 38 | 39 | foreach ($modules as $module) 40 | { 41 | if ($module) 42 | { 43 | if ($this->app['files']->exists($module->path('assets'))) 44 | { 45 | // Group path 46 | $groupPath = $module->def('group') ? str_replace('/', '_', $module->def('group')) : null; 47 | 48 | // Prepare params 49 | $path = ltrim(str_replace(app()->make('path.base'), '', $module->path()), "/") . "/assets"; 50 | 51 | // Get destination path 52 | if (is_array($this->app['config']->get('modules::path'))) 53 | { 54 | $destination = app()->make('path.public') . '/packages/module/' . $groupPath . '/' . $module->name() . '/assets'; 55 | } 56 | else 57 | { 58 | $destination = app()->make('path.public') . '/packages/module/' . $module->name() . '/assets'; 59 | } 60 | 61 | // Try to copy 62 | $success = $this->app['files']->copyDirectory($path, $destination); 63 | 64 | // Result 65 | if ( ! $success) $this->line("Unable to publish assets for module '" . $module->name() . "'"); 66 | else $this->info("Published assets for module '" . $module->name() . "'"); 67 | } 68 | else 69 | { 70 | $this->line("Module '" . $module->name() . "' has no assets available."); 71 | } 72 | } 73 | else 74 | { 75 | $this->error("Module '" . $moduleName . "' does not exist."); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * Get the console command arguments. 82 | * @return array 83 | */ 84 | protected function getArguments() 85 | { 86 | return array( 87 | array('module', InputArgument::OPTIONAL, 'The name of module being published.'), 88 | ); 89 | } 90 | 91 | /** 92 | * Get the console command options. 93 | * @return array 94 | */ 95 | protected function getOptions() 96 | { 97 | return array(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | package('creolab/laravel-modules', 'modules', __DIR__); 18 | 19 | // Register commands 20 | $this->bootCommands(); 21 | 22 | try 23 | { 24 | // Auto scan if specified 25 | $this->app['modules']->start(); 26 | 27 | // And finally register all modules 28 | $this->app['modules']->register(); 29 | } 30 | catch (\Exception $e) 31 | { 32 | $this->app['modules']->logError("There was an error when starting modules: [".$e->getMessage()."]"); 33 | } 34 | } 35 | 36 | /** 37 | * Register the service provider. 38 | * @return void 39 | */ 40 | public function register() 41 | { 42 | // Register IoC bindings 43 | $this->app['modules'] = $this->app->share(function($app) 44 | { 45 | return new Finder($app, $app['files'], $app['config']); 46 | }); 47 | } 48 | 49 | /** 50 | * Register all available commands 51 | * @return void 52 | */ 53 | public function bootCommands() 54 | { 55 | // Add modules command 56 | $this->app['modules.list'] = $this->app->share(function($app) 57 | { 58 | return new Commands\ModulesCommand($app); 59 | }); 60 | 61 | // Add scan command 62 | $this->app['modules.scan'] = $this->app->share(function($app) 63 | { 64 | return new Commands\ModulesScanCommand($app); 65 | }); 66 | 67 | // Add publish command 68 | $this->app['modules.publish'] = $this->app->share(function($app) 69 | { 70 | return new Commands\ModulesPublishCommand($app); 71 | }); 72 | 73 | // Add migrate command 74 | $this->app['modules.migrate'] = $this->app->share(function($app) 75 | { 76 | return new Commands\ModulesMigrateCommand($app); 77 | }); 78 | 79 | // Add seed command 80 | $this->app['modules.seed'] = $this->app->share(function($app) 81 | { 82 | return new Commands\ModulesSeedCommand($app); 83 | }); 84 | 85 | // Add create command 86 | $this->app['modules.create'] = $this->app->share(function($app) 87 | { 88 | return new Commands\ModulesCreateCommand($app); 89 | }); 90 | 91 | // Add generate command 92 | $this->app['modules.generate'] = $this->app->share(function($app) 93 | { 94 | return new Commands\ModulesGenerateCommand($app); 95 | }); 96 | 97 | // Now register all the commands 98 | $this->commands(array( 99 | 'modules.list', 100 | 'modules.scan', 101 | 'modules.publish', 102 | 'modules.migrate', 103 | 'modules.seed', 104 | 'modules.create', 105 | 'modules.generate' 106 | )); 107 | } 108 | 109 | /** 110 | * Provided service 111 | * @return array 112 | */ 113 | public function provides() 114 | { 115 | return array('Modules'); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/Commands/ModulesMigrateCommand.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ModulesMigrateCommand extends AbstractCommand { 16 | 17 | /** 18 | * Name of the command 19 | * 20 | * @var string 21 | */ 22 | protected $name = 'modules:migrate'; 23 | 24 | /** 25 | * Command description 26 | * 27 | * @var string 28 | */ 29 | protected $description = 'Run migrations for modules.'; 30 | 31 | /** 32 | * List of migrations 33 | * 34 | * @var array 35 | */ 36 | protected $migrationList = array(); 37 | 38 | /** 39 | * Execute the console command. 40 | * 41 | * @return void 42 | */ 43 | public function fire() 44 | { 45 | $this->info('Migrating modules'); 46 | 47 | // Get all modules or 1 specific 48 | if ($moduleName = $this->input->getArgument('module')) 49 | { 50 | $modules = array(app('modules')->module($moduleName)); 51 | } 52 | else 53 | { 54 | $modules = app('modules')->modules(); 55 | } 56 | 57 | foreach ($modules as $module) 58 | { 59 | if ($module) 60 | { 61 | if ($this->app['files']->exists($module->path('migrations'))) 62 | { 63 | // Prepare params 64 | $path = ltrim(str_replace(app()->make('path.base'), '', $module->path()), "/") . "/migrations"; 65 | $_info = array('path' => $path); 66 | 67 | // Add to migration list 68 | array_push($this->migrationList, $_info); 69 | $this->info("Added '" . $module->name() . "' to migration list."); 70 | 71 | } 72 | else 73 | { 74 | $this->line("Module '" . $module->name() . "' has no migrations."); 75 | } 76 | } 77 | else 78 | { 79 | $this->error("Module '" . $moduleName . "' does not exist."); 80 | } 81 | } 82 | 83 | if (count($this->migrationList)) 84 | { 85 | $this->info(" 86 | Running Migrations..."); 87 | 88 | // Process migration list 89 | $this->runPathsMigration(); 90 | } 91 | 92 | if ($this->input->getOption('seed')) 93 | { 94 | $this->info("Running Seeding Command..."); 95 | $this->call('modules:seed'); 96 | } 97 | } 98 | 99 | /** 100 | * Run paths migrations 101 | * 102 | * @return void 103 | */ 104 | protected function runPathsMigration() 105 | { 106 | $_fileService = new Filesystem(); 107 | $_tmpPath = app_path('storage') . DIRECTORY_SEPARATOR . 'migrations'; 108 | 109 | if (!is_dir($_tmpPath) && !$_fileService->exists($_tmpPath)) 110 | { 111 | $_fileService->mkdir($_tmpPath); 112 | } 113 | 114 | $this->info("Gathering migration files to {$_tmpPath}"); 115 | 116 | // Copy all files to storage/migrations 117 | foreach ($this->migrationList as $migration) 118 | { 119 | $_fileService->mirror($migration['path'], $_tmpPath); 120 | } 121 | 122 | //call migrate command on temporary path 123 | $this->info("Migrating..."); 124 | 125 | $opts = array('--path' => ltrim(str_replace(base_path(), '', $_tmpPath), '/')); 126 | 127 | if($this->input->getOption('force')) { 128 | $opts['--force'] = true; 129 | } 130 | 131 | if ($this->input->getOption('database')) { 132 | $opts['--database'] = $this->input->getOption('database'); 133 | } 134 | 135 | $this->call('migrate', $opts); 136 | 137 | // Delete all temp migration files 138 | $this->info("Cleaning temporary files"); 139 | $_fileService->remove($_tmpPath); 140 | 141 | // Done 142 | $this->info("DONE!"); 143 | 144 | } 145 | 146 | /** 147 | * Get the console command arguments. 148 | * 149 | * @return array 150 | */ 151 | protected function getArguments() 152 | { 153 | return array( 154 | array('module', InputArgument::OPTIONAL, 'The name of module being migrated.'), 155 | ); 156 | } 157 | 158 | /** 159 | * Get the console command options. 160 | * 161 | * @return array 162 | */ 163 | protected function getOptions() 164 | { 165 | return array( 166 | array('seed', null, InputOption::VALUE_NONE, 'Indicates if the module should seed the database.'), 167 | array('force', '-f', InputOption::VALUE_NONE, 'Force the operation to run when in production.'), 168 | array('database', null, InputOption::VALUE_OPTIONAL, 'The database connection.', null) 169 | ); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/Finder.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Finder { 10 | 11 | /** 12 | * Modules collection 13 | * @var ModuleCollection 14 | */ 15 | protected $modules; 16 | 17 | /** 18 | * The modules manifest 19 | * @var Manifest 20 | */ 21 | protected $manifest; 22 | 23 | /** 24 | * IoC 25 | * @var Illuminate\Foundation\Application 26 | */ 27 | protected $app; 28 | 29 | /** 30 | * Initialize the finder 31 | * @param Application $app 32 | */ 33 | public function __construct(Application $app) 34 | { 35 | $this->app = $app; 36 | $this->modules = new ModuleCollection($app); 37 | $this->manifest = new Manifest($app); 38 | } 39 | 40 | /** 41 | * Start finder 42 | * @return void 43 | */ 44 | public function start() 45 | { 46 | if ($this->app['config']->get('modules::mode') == 'auto') 47 | { 48 | $this->app['modules']->scan(); 49 | } 50 | elseif ($this->app['config']->get('modules::mode') == 'manifest') 51 | { 52 | if ($manifest = $this->manifest->toArray()) 53 | { 54 | $this->app['modules']->manual($this->manifest->toArray()); 55 | } 56 | else 57 | { 58 | $this->app['modules']->scan(); 59 | } 60 | } 61 | else 62 | { 63 | $this->app['modules']->manual(); 64 | } 65 | } 66 | 67 | /** 68 | * Return module collection 69 | * @return ModuleCollection 70 | */ 71 | public function modules() 72 | { 73 | return $this->modules; 74 | } 75 | 76 | /** 77 | * Return single module 78 | * @param string $id 79 | * @return Module 80 | */ 81 | public function module($id) 82 | { 83 | if (isset($this->modules[$id])) return $this->modules[$id]; 84 | } 85 | 86 | /** 87 | * Scan module folder and add valid modules to collection 88 | * @return array 89 | */ 90 | public function scan() 91 | { 92 | // Get the modules directory paths 93 | $modulesPaths = $this->app['config']->get('modules::path'); 94 | if ( ! is_array($modulesPaths)) $modulesPaths = array($modulesPaths); 95 | 96 | // Now prepare an array with all directories 97 | $paths = array(); 98 | foreach ($modulesPaths as $modulesPath) $paths[$modulesPath] = $this->app['files']->directories(base_path($modulesPath)); 99 | 100 | if ($paths) 101 | { 102 | foreach ($paths as $path => $directories) 103 | { 104 | if ($directories) 105 | { 106 | foreach ($directories as $directory) 107 | { 108 | // Check if dir contains a module definition file 109 | if ($this->app['files']->exists($directory . '/module.json')) 110 | { 111 | $name = pathinfo($directory, PATHINFO_BASENAME); 112 | $this->modules[$name] = new Module($name, $directory, null, $this->app, $path); 113 | } 114 | } 115 | } 116 | } 117 | 118 | // Save the manifest file 119 | $this->saveManifest(); 120 | } 121 | 122 | return $this->modules; 123 | } 124 | 125 | /** 126 | * Get modules from config array 127 | * @return array 128 | */ 129 | public function manual($config = null) 130 | { 131 | if (! is_null($config)) 132 | { 133 | $this->createInstances($config); 134 | } 135 | 136 | else 137 | { 138 | $moduleGroups = $this->app['config']->get('modules::modules'); 139 | 140 | if ($moduleGroups) 141 | { 142 | foreach ($moduleGroups as $group => $modules) 143 | { 144 | $this->createInstances($modules, $group); 145 | } 146 | } 147 | } 148 | 149 | return $this->modules; 150 | } 151 | 152 | /** 153 | * Create module instances 154 | * @param array $modules 155 | * @param string|null $groupPath 156 | * @return array 157 | */ 158 | public function createInstances($modules, $groupPath = null) 159 | { 160 | foreach ($modules as $key => $module) 161 | { 162 | // Get name and defintion 163 | if (is_string($module)) 164 | { 165 | $name = $module; 166 | 167 | $definition = array(); 168 | } 169 | 170 | elseif (is_array($module)) 171 | { 172 | $name = $key; 173 | 174 | $definition = $module; 175 | } 176 | 177 | // Get group. Manifest mode has group defined on the module. 178 | $group = (! is_null($groupPath)) ? $groupPath : $module['group']; 179 | 180 | // The path 181 | $path = base_path($group . '/' . $name); 182 | 183 | // Create instance 184 | $this->modules[$name] = new Module($name, $path, $definition, $this->app, $group); 185 | } 186 | } 187 | 188 | /** 189 | * Return manifest object 190 | * @return Manifest 191 | */ 192 | public function manifest($module = null) 193 | { 194 | return $this->manifest->toArray($module); 195 | } 196 | 197 | /** 198 | * Save the manifest file 199 | * @param array $modules 200 | * @return void 201 | */ 202 | public function saveManifest($modules = null) 203 | { 204 | $this->manifest->save($this->modules); 205 | } 206 | 207 | /** 208 | * Delete the manifest file 209 | * @return void 210 | */ 211 | public function deleteManifest() 212 | { 213 | $this->manifest->delete(); 214 | } 215 | 216 | /** 217 | * Register all modules in collection 218 | * @return void 219 | */ 220 | public function register() 221 | { 222 | return $this->modules->registerModules(); 223 | } 224 | 225 | /** 226 | * Log a debug message 227 | * @param string $message 228 | * @return void 229 | */ 230 | public function logDebug($message) 231 | { 232 | $this->log($message); 233 | } 234 | 235 | /** 236 | * Log an error message 237 | * @param string $message 238 | * @return void 239 | */ 240 | public function logError($message) 241 | { 242 | $this->log($message, 'error'); 243 | } 244 | 245 | /** 246 | * Log a message 247 | * @param string $type 248 | * @param string $message 249 | * @return void 250 | */ 251 | public function log($message, $type = 'debug') 252 | { 253 | if ($this->app['config']->get('modules::debug')) 254 | { 255 | $namespace = 'MODULES'; 256 | $message = "[$namespace] $message"; 257 | 258 | if ($type == 'error') $this->app['log']->error($message); 259 | else $this->app['log']->debug($message); 260 | } 261 | } 262 | 263 | /** 264 | * Prettify a JSON Encode ( PHP 5.4+ ) 265 | * @param mixed $values 266 | * @return string 267 | */ 268 | public function prettyJsonEncode($values) 269 | { 270 | return version_compare(PHP_VERSION, '5.4.0', '>=') ? json_encode($values, JSON_PRETTY_PRINT) : json_encode($values); 271 | } 272 | 273 | } -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Module extends \Illuminate\Support\ServiceProvider { 11 | 12 | /** 13 | * Name of the module 14 | * @var string 15 | */ 16 | protected $name; 17 | 18 | /** 19 | * Path to module directory 20 | * @var string 21 | */ 22 | protected $path; 23 | 24 | /** 25 | * Path to module definition JSON file 26 | * @var string 27 | */ 28 | protected $definitionPath; 29 | 30 | /** 31 | * Module definition 32 | * @var array 33 | */ 34 | protected $definition; 35 | 36 | /** 37 | * Is the module enabled 38 | * @var boolean 39 | */ 40 | protected $enabled = true; 41 | 42 | /** 43 | * Order to register the module 44 | * @var integer 45 | */ 46 | public $order = 0; 47 | 48 | /** 49 | * IoC 50 | * @var Illuminate\Foundation\Application 51 | */ 52 | protected $app; 53 | 54 | /** 55 | * Path for module group 56 | * @var string 57 | */ 58 | public $group; 59 | 60 | /** 61 | * Initialize a module 62 | * @param Application $app 63 | */ 64 | public function __construct($name, $path = null, $definition = null, Application $app, $group = null) 65 | { 66 | $this->name = $name; 67 | $this->app = $app; 68 | $this->path = $path; 69 | $this->group = $group; 70 | 71 | // Get definition 72 | if ($path and ! $definition) 73 | { 74 | $this->definitionPath = $path . '/module.json'; 75 | } 76 | elseif (is_array($definition)) 77 | { 78 | $this->definition = $definition; 79 | } 80 | 81 | // Try to get the definition 82 | $this->readDefinition(); 83 | } 84 | 85 | /** 86 | * Read the module definition 87 | * @return array 88 | */ 89 | public function readDefinition() 90 | { 91 | // Read mode from configuration 92 | $mode = $this->app['config']->get('modules::mode'); 93 | 94 | if ($mode == 'auto' or ($mode == 'manifest' and ! $this->app['modules']->manifest())) 95 | { 96 | if ($this->definitionPath) 97 | { 98 | $this->definition = @json_decode($this->app['files']->get($this->definitionPath), true); 99 | 100 | if ( ! $this->definition or (isset($this->definition['enabled']) and $this->definition['enabled'] === false)) 101 | { 102 | $this->enabled = false; 103 | } 104 | } 105 | else 106 | { 107 | $this->enabled = false; 108 | } 109 | } 110 | else 111 | { 112 | if ((isset($this->definition['enabled']) and $this->definition['enabled'] === false)) 113 | { 114 | $this->enabled = false; 115 | } 116 | } 117 | 118 | // Add name to defintion 119 | if ( ! isset($this->definition['name'])) $this->definition['name'] = $this->name; 120 | 121 | // Assign order number 122 | if ( ! isset($this->definition['order'])) $this->definition['order'] = $this->order = 0; 123 | else $this->definition['order'] = $this->order = (int) $this->definition['order']; 124 | 125 | // Add group to definition 126 | $this->definition['group'] = $this->group; 127 | 128 | return $this->definition; 129 | } 130 | 131 | /** 132 | * Register the module if enabled 133 | * @return boolean 134 | */ 135 | public function register() 136 | { 137 | if ($this->enabled) 138 | { 139 | // Register module as a package 140 | $this->package('modules/' . $this->name, $this->name, $this->path()); 141 | 142 | // Register service provider 143 | $this->registerProviders(); 144 | 145 | // Get files for inclusion 146 | $moduleInclude = (array) array_get($this->definition, 'include'); 147 | $globalInclude = $this->app['config']->get('modules::include'); 148 | $include = array_merge($globalInclude, $moduleInclude); 149 | 150 | // Include all of them if they exist 151 | foreach ($include as $file) 152 | { 153 | $path = $this->path($file); 154 | if ($this->app['files']->exists($path)) require $path; 155 | } 156 | 157 | // Register alias(es) into artisan 158 | if(!is_null($this->def('alias'))) { 159 | $aliases = $this->def('alias'); 160 | 161 | if(!is_array($aliases)) 162 | $aliases = array($aliases); 163 | 164 | foreach($aliases as $alias => $facade) { 165 | AliasLoader::getInstance()->alias($alias, $facade); 166 | } 167 | } 168 | 169 | // Register command(s) into artisan 170 | if(!is_null($this->def('command'))) { 171 | $commands = $this->def('command'); 172 | 173 | if(!is_array($commands)) 174 | $commands = array($commands); 175 | 176 | $this->commands($commands); 177 | } 178 | 179 | // Log it 180 | $this->app['modules']->logDebug('Module "' . $this->name . '" has been registered.'); 181 | } 182 | } 183 | 184 | /** 185 | * Register service provider for module 186 | * @return void 187 | */ 188 | public function registerProviders() 189 | { 190 | $providers = $this->def('provider'); 191 | 192 | if ($providers) 193 | { 194 | if (is_array($providers)) 195 | { 196 | foreach ($providers as $provider) 197 | { 198 | $this->app->register($instance = new $provider($this->app)); 199 | } 200 | } 201 | else 202 | { 203 | $this->app->register($instance = new $providers($this->app)); 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * Run the seeder if it exists 210 | * @return void 211 | */ 212 | public function seed() 213 | { 214 | $class = $this->def('seeder'); 215 | 216 | if (class_exists($class)) 217 | { 218 | $seeder = new $class; 219 | $seeder->run(); 220 | } 221 | } 222 | 223 | /** 224 | * Return name of module 225 | * @return string 226 | */ 227 | public function name() 228 | { 229 | return $this->name; 230 | } 231 | 232 | /** 233 | * Module path 234 | * @param string $path 235 | * @return string 236 | */ 237 | public function path($path = null) 238 | { 239 | if ($path) return $this->path . '/' . ltrim($path, '/'); 240 | else return $this->path; 241 | } 242 | 243 | /** 244 | * Check if module is enabled 245 | * @return boolean 246 | */ 247 | public function enabled() 248 | { 249 | return (bool) $this->enabled; 250 | } 251 | 252 | /** 253 | * Get definition value 254 | * @param string $key 255 | * @return mixed 256 | */ 257 | public function def($key = null) 258 | { 259 | if ( ! isset($this->definition['enabled'])) $this->definition['enabled'] = $this->enabled; 260 | 261 | if ($key) return isset($this->definition[$key]) ? $this->definition[$key] : null; 262 | else return $this->definition; 263 | } 264 | 265 | } 266 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Abandoned 2 | 3 | **This package is no longer maintained.** 4 | 5 | I encourage you to use composer packages for your modules to get the most flexibility out of it. 6 | 7 | # Modules in Laravel 4 8 | 9 | Application specific modules in Laravel 4 can be enabled by adding the following to your **"composer.json"** file: 10 | 11 | "require": { 12 | "creolab/laravel-modules": "dev-master" 13 | } 14 | 15 | And by adding a new provider to your providers list in **"app/config/app.php"**: 16 | 17 | 'providers' => array( 18 | 'Creolab\LaravelModules\ServiceProvider', 19 | ); 20 | 21 | Also you need to add your modules directory to the composer autoloader: 22 | 23 | "autoload": { 24 | "classmap": [ 25 | "app/modules" 26 | ] 27 | } 28 | 29 | This also means you need to run **"composer dump-autoload"** every time you add a new class to your module. 30 | 31 | By default you can add a **"modules"** directory in your **"app"** directory. So as an example this is a structure for one of my projects: 32 | 33 | app/ 34 | |-- modules 35 | |-- auth 36 | |-- controllers 37 | |-- models 38 | |-- views 39 | |-- module.json 40 | |-- content 41 | |-- controllers 42 | |-- models 43 | |-- views 44 | |-- module.json 45 | |-- shop 46 | |-- module.json 47 | |-- system 48 | |-- module.json 49 | 50 | Note that every module has a **"module.json"** file, in which you can enable/disable the module. I plan on adding more meta data to these module definitions, but I need feedback as to what to put in there. 51 | The first thing will probably be some kind of a bootstrap class. 52 | 53 | If you want to have your modules in more that 1 directories you need to change the packages config file as following: 54 | 55 | 'path' => array( 56 | 'app/modules', 57 | 'public/site', 58 | 'another/folder/containing/modules', 59 | ), 60 | 61 | And don't forget to add those directories to your autoload list inside the composer.json file. 62 | 63 | One of the available option is the order in which the modules are loaded. This can be done simply by adding the following to your module.json file: 64 | 65 | "order": 5 66 | 67 | The order defaults to 0, so keep that in mind if you don't define it. 68 | 69 | For now take a look at the example implementation, and please provide feedback ;) 70 | 71 | [https://github.com/bstrahija/laravel-modules-example](https://github.com/bstrahija/laravel-modules-example) 72 | 73 | # Commands 74 | 75 | There are 2 commands available through this package: 76 | 77 | php artisan modules 78 | 79 | Which simply diplays all current modules depending on the mode set in the configuration. And: 80 | 81 | php artisan modules:scan 82 | 83 | Which is only required if you have your modules setup in the **"manifest"** mode (see below). 84 | This command scans the modules exactly like in the **"auto"** mode, but caches the results into a manifest file. 85 | 86 | # Optimization 87 | 88 | By default the package scans the **"modules"** directory for **"module.json"** files. This is not the best solution way to discover modules, and I do plan to implement some kind of a caching to the Finder class. 89 | To optimize the modules Finder even more you can publish the package configuration, and add the modules and definitions directly inside the configuration file by running: 90 | 91 | php artisan config:publish creolab/laravel-modules 92 | 93 | And the editing the file **"app/config/packages/creolab/laravel-modules/config.php"**. 94 | You just need to change the **"mode"** parameter from **"auto"** to **"manual"**, and list your modules under the **"modules"** key. An example of that is already provided inside the configuration. 95 | 96 | **Note for Manual mode with multiple paths** : Laravel-Modules could not determine witch path to use. So please specify the folder containing the module you want to load. Like this example : 97 | 98 | 'modules' => [ 99 | 'app/modules' => [ 100 | 'system' => ['enabled' => true], 101 | ], 102 | 'another/modules/path' => [ 103 | 'pages' => ['enabled' => false], 104 | 'seo' => ['enabled' => true], 105 | ], 106 | ], 107 | 108 | You can also add multiple module paths as an array, but do note that if a module has the same name, there will be problems. 109 | 110 | ## Including files 111 | 112 | You can also specify which files in the module will be automatically included. Simply add a list of files inside your **module.json** config: 113 | 114 | { 115 | "include": [ 116 | "breadcrumbs.php" 117 | ] 118 | } 119 | 120 | There are some defaults set on which files will be included if they exist. Take a look at the latest config file, and republish the configuration if needed. By default these files will be included: 121 | 122 | 'include' => array( 123 | 'helpers.php', 124 | 'filters.php', 125 | 'composers.php', 126 | 'routes.php', 127 | 'bindings.php', 128 | 'observers.php', 129 | ) 130 | 131 | So you have the choice to either add your custom files to the global configuration, which will look for these files in every module, or you can set it up on a per module basis by adding it to the **module.json** file. 132 | 133 | ## Service providers 134 | 135 | A new addition is registering service providers for each module. Just add a line to your **module.json** file that looks something like this: 136 | 137 | "provider": "App\\Modules\\Content\\ServiceProvider" 138 | 139 | These service provider classes work exactly like any other service provider added to your **app/config/app.php** configuration, so setup these classes by extending the **\Illuminate\Support\ServiceProvider** class and adding the appropriate methods. 140 | 141 | You can also register multiple providers for every module by simply providing an array: 142 | 143 | "provider": [ 144 | "App\\Modules\\Content\\ServiceProvider", 145 | "App\\Modules\\Content\\AnotherServiceProvider" 146 | ] 147 | 148 | Keep in mind that you may have to run **composer dump-autoload** so you want get error on missing classes. 149 | 150 | ## Modules Manifest 151 | 152 | Another possible mode is **"manifest"** which basically writes a JSON manifest file in your Laravel storage directory that contains all the modules definitions. 153 | This is only done the first time and to update the manifest file you need to either delete it, or rescan the modules via the following command: 154 | 155 | php artisan modules:scan 156 | 157 | # Assets 158 | 159 | Just recently the ability to publish public assets for each module has been added. Just run: 160 | 161 | php artisan modules:publish 162 | 163 | And all modules that contain an **"assets"** directory will be published to the Laravel public directory. 164 | You can also publish assets for individual modules by providing the module name: 165 | 166 | php artisan modules:publish content 167 | 168 | # Migrations 169 | 170 | Every module can have it's own migrations, and they need to be in the module/migrations directory. So for example if you want to create a migration that creates a user table for the auth module: 171 | 172 | php artisan migrate:make create_users_table --path=app/modules/auth/migrations --table=users --create 173 | 174 | And to run all module migrations do this: 175 | 176 | php artisan modules:migrate 177 | 178 | Or to run migrations for a specific module: 179 | 180 | php artisan modules:migrate auth 181 | 182 | You can also seed the database form the module if your **module.json** contains a seeder setting. Just pass the **--seed** option to the command: 183 | 184 | php artisan modules:migrate --seed 185 | 186 | # Seeding 187 | 188 | The modules can also have seeders. Just create the class like you would create a normal seeder, place it somewhere inside your module, and be sure to run **composer dump-autoload**. Then add the following to your **module.json** file: 189 | 190 | "seeder": "App\\Modules\\Content\\Seeds\\DatabaseSeeder" 191 | 192 | This setting should contain the namespace path to your seeder class. Now simply run: 193 | 194 | php artisan modules:seed 195 | 196 | To seed all your modules. Or you can do it for a specific module: 197 | 198 | php artisan modules:seed content 199 | 200 | # Commands 201 | 202 | You can add module specific commands. This is a sample artisan command file creation : 203 | 204 | php artisan command:make --path=app/modules//commands --namespace=App\Modules\\Commands --command=modules.mymodule:mycommand 205 | 206 | Then in the **module.json** add (you can also add an array if you have multiple commands) : 207 | 208 | "command": "App\\Modules\\\\Commands\\MyModuleCommandName" 209 | 210 | After a dump-autoload you can now execute **modules.mymodule:mycommand** from command line : 211 | 212 | php artisan modules.mymodule:mycommand 213 | 214 | # Aliases 215 | 216 | If you declare Facades into your modules you will like to create Aliases for your module, you can simply reference your alias in the `module.json` : 217 | 218 | "alias": { 219 | "" "App\\Modules\\\\Facades\\" 220 | } 221 | 222 | # License 223 | 224 | Thi package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 225 | --------------------------------------------------------------------------------