diff --git a/docs/commands.md b/docs/commands.md index be67a6f..3433258 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -8,12 +8,12 @@ Summary of all the commands: ### Create new module -`php artisan laragine:module ModuleName` +`php artisan laragine:module ModuleName` | optional: add `--plugins` to be in the Plugins directory ### Initialize the unit -`php artisan laragine:unit UnitName --module=ModuleName --init` +`php artisan laragine:unit UnitName --module=ModuleName --init` | optional: add `--plugins` to be in the Plugins directory ### Create all the related stuff for the unit -`php artisan laragine:unit UnitName --module=ModuleName` \ No newline at end of file +`php artisan laragine:unit UnitName --module=ModuleName` | optional: add `--plugins` to be in the Plugins directory \ No newline at end of file diff --git a/docs/config.md b/docs/config.md index 3d68d33..f38a4d1 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,3 +1,3 @@ ## Config -You will notice in any module you generate, a `config` directory, so basically you can add configuration for the module, and you can access it in this form: `config('core_modulename.some_key')` for example: `config('core_base.api_key')` \ No newline at end of file +You will notice in any module you generate, a `config` directory, so basically you can add configuration for the module, and you can access it in this form: `config('coreOrplugins_moduleName.some_key')` for example: `config('core_base.api_key')` another example `config('plugins_sale.tax')` \ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md index 522360b..70760ce 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -22,7 +22,9 @@ It's very important to know why to use Laragine, here is why: ![Structure](structure.png) -The `Base` module is the main module in which every new module will be created (keep reading the other sections to know how), will be derived from. +The `Base` module in the `core` directory is the main module in which every new module will be created (keep reading the other sections to know how), will be derived from. + +Also it's worth mentioning that there is another directory called `plugins` for overriding current modules, adding extra functionalities or adding new modules to the system. It's also very important to understand the following terms: @@ -34,7 +36,7 @@ It's also very important to understand the following terms: * Laragine currently is working on **Laravel 8.x, 9.x, 10.x and 11.x** -* Laragine directory will be in the root directory under `Core` directory +* Laragine directory will be in the root directory under `core` directory and as mentioned above there is also `plugins` directory * The system response (including errors response if you applied what's in `Error Handling` section) to any request will be as in below examples (`status_code` is the http status code): diff --git a/docs/plugins.md b/docs/plugins.md new file mode 100644 index 0000000..76230e5 --- /dev/null +++ b/docs/plugins.md @@ -0,0 +1,20 @@ +## Plugins + +**Note:** it's important to read the module and unit sections first + +To create a new module or a new unit to the plugins directory you can do so by adding `--plugins` option to the commands + +Here is a complete example: + +Creating New module: +```bash +php artisan laragine:module Todo --plugins +``` +Initializing a unit: +```bash +php artisan laragine:unit Task --module=Todo --init --plugins +``` +Publishing the unit: +```bash +php artisan laragine:unit Task --module=Todo --plugins +``` \ No newline at end of file diff --git a/docs/structure.PNG b/docs/structure.PNG index c77b0db..aba8fd6 100644 Binary files a/docs/structure.PNG and b/docs/structure.PNG differ diff --git a/src/Commands/MakeModule.php b/src/Commands/MakeModule.php index 42cc503..665a647 100644 --- a/src/Commands/MakeModule.php +++ b/src/Commands/MakeModule.php @@ -12,14 +12,14 @@ class MakeModule extends Command * * @var string */ - protected $signature = 'laragine:module {name}'; + protected $signature = 'laragine:module {name} {--P|plugins}'; /** * The console command description. * * @var string */ - protected $description = 'Create new module'; + protected $description = 'Create a new module'; /** * Create a new command instance. @@ -33,7 +33,7 @@ public function __construct() public function handle() { - $command = GeneratorFactory::create($this, 'MakeModule', $this->argument('name')); + $command = GeneratorFactory::create($this, 'MakeModule', $this->argument('name'), $this->option('plugins')); $command->run(); } } diff --git a/src/Commands/MakeUnit.php b/src/Commands/MakeUnit.php index f900b4d..a5d9a4d 100644 --- a/src/Commands/MakeUnit.php +++ b/src/Commands/MakeUnit.php @@ -12,14 +12,14 @@ class MakeUnit extends Command * * @var string */ - protected $signature = 'laragine:unit {name} {--module=} {--I|init}'; + protected $signature = 'laragine:unit {name} {--module=} {--I|init} {--P|plugins}'; /** * The console command description. * * @var string */ - protected $description = 'Create new unit'; + protected $description = 'Create a new unit'; /** * Create a new command instance. @@ -48,7 +48,8 @@ public function handle() 'MakeUnit', $this->argument('name'), $this->option('module'), - $this->option('init') + $this->option('init'), + $this->option('plugins') ); $command->run(); diff --git a/src/Core/Base/File.stub b/src/Core/Base/File.stub index 4715e3c..9e7597e 100644 --- a/src/Core/Base/File.stub +++ b/src/Core/Base/File.stub @@ -9,11 +9,11 @@ trait File * * @param string $dir * @param string $module - * @param boolean $is_core + * @param boolean $is_plugins */ - protected function loadFiles($dir, $module = 'base', $is_core = true) + protected function loadFiles($dir, $module = 'base', $is_plugins = false) { - $module_root_dir = $is_core ? 'core' : 'plugins'; + $module_root_dir = $is_plugins ? 'plugins' : 'core'; $this->loadRoutesFrom($dir . '/routes/web.php'); $this->loadRoutesFrom($dir . '/routes/api.php'); diff --git a/src/Core/Base/Module.stub b/src/Core/Base/Module.stub index d6063ed..6fc06fa 100644 --- a/src/Core/Base/Module.stub +++ b/src/Core/Base/Module.stub @@ -2,23 +2,43 @@ namespace Core\Base\Traits\ServiceProvider; +use Illuminate\Support\Str; + trait Module { /** * register the service provider for the module - * - * @param string $excluded_directory - * @param string $root_namespace */ - private function registerModules($excluded_directory = 'Base', $root_namespace = 'Core') + private function registerModules() { - $base_path = base_path() . '/core'; - foreach (glob($base_path.'/*/ModuleServiceProvider.php') as $file) { + // Register the core modules + $root_namespace = 'Core'; + $excluded_directory = 'Base'; + $root_path = config('laragine.root_dir'); + + foreach (glob($root_path.'/*/ModuleServiceProvider.php') as $file) { if (!preg_match("/core\/{$excluded_directory}/i", $file)) { - $namespace = str_replace('/', '\\', str_replace('.php', '', $file)); - $namespace = explode('core\\', $namespace)[1]; - $this->app->register($root_namespace . '\\' . $namespace); + $this->registerModuleServiceProvider($file, $root_namespace); } } + + // Register the plugins modules + $plugins_namespace = 'Plugins'; + $plugins_path = config('laragine.plugins_dir'); + + foreach (glob($plugins_path.'/*/ModuleServiceProvider.php') as $file) { + $this->registerModuleServiceProvider($file, $plugins_namespace); + } + } + + /** + * handle registering the module service provider + */ + private function registerModuleServiceProvider($file, $root_namespace) + { + $root_namespace_lower_case = Str::lower($root_namespace); + $namespace = str_replace('/', '\\', str_replace('.php', '', $file)); + $namespace = explode("{$root_namespace_lower_case}\\", $namespace)[1]; + $this->app->register($root_namespace . '\\' . $namespace); } } diff --git a/src/Core/Module/ModuleServiceProvider.stub b/src/Core/Module/ModuleServiceProvider.stub index 614f7f4..717b0fa 100644 --- a/src/Core/Module/ModuleServiceProvider.stub +++ b/src/Core/Module/ModuleServiceProvider.stub @@ -1,6 +1,6 @@ loadFiles(__DIR__, '#UNIT_NAME_LOWER_CASE#'); + $this->loadFiles(__DIR__, '#UNIT_NAME_LOWER_CASE#'#IS_PLUGINS#); } /** diff --git a/src/Core/Module/Unit.stub b/src/Core/Module/Unit.stub index cce6c69..2e652b6 100644 --- a/src/Core/Module/Unit.stub +++ b/src/Core/Module/Unit.stub @@ -1,6 +1,6 @@ 'api', 'middleware' => []], function () { # V1 - Route::namespace('Core\#MODULE_NAME#\Controllers\API\V1')->prefix('v1')->name('api.v1.')->group(function () { + Route::namespace('#SELECTED_DIRECTORY#\#MODULE_NAME#\Controllers\API\V1')->prefix('v1')->name('api.v1.')->group(function () { #*** Ex: START: #UNIT_NAME# ***# // Route::apiResource('#UNIT_NAME_PLURAL_LOWER_CASE#', '#UNIT_NAME#Controller'); #*** END: #UNIT_NAME# ***# diff --git a/src/Core/Module/web.stub b/src/Core/Module/web.stub index 23adc35..4a14d24 100644 --- a/src/Core/Module/web.stub +++ b/src/Core/Module/web.stub @@ -1,6 +1,6 @@ prefix('admin')->name('admin.')->group(function () { +Route::namespace('#SELECTED_DIRECTORY#\#MODULE_NAME#\Controllers\Web')->prefix('admin')->name('admin.')->group(function () { #*** Ex: START: #UNIT_NAME# ***# // Route::resource('#UNIT_NAME_PLURAL_LOWER_CASE#', '#UNIT_NAME#Controller')->except([ // 'store', 'update', 'destroy' diff --git a/src/Generators/Payloads/Commands/Base.php b/src/Generators/Payloads/Commands/Base.php index 11738fc..1f24af1 100644 --- a/src/Generators/Payloads/Commands/Base.php +++ b/src/Generators/Payloads/Commands/Base.php @@ -28,6 +28,13 @@ class Base implements GeneratorInterface */ protected $root_dir; + /** + * the plugins dir + * + * @var string + */ + protected $plugins_dir; + /** * init * @@ -40,6 +47,7 @@ public function __construct(Command $command, $args) $this->command = $command; $this->args = $args; $this->root_dir = config('laragine.root_dir'); + $this->plugins_dir = config('laragine.plugins_dir'); } /** diff --git a/src/Generators/Payloads/Commands/Install.php b/src/Generators/Payloads/Commands/Install.php index b551727..a0923c2 100644 --- a/src/Generators/Payloads/Commands/Install.php +++ b/src/Generators/Payloads/Commands/Install.php @@ -15,7 +15,7 @@ public function run() { $allow_publish = true; if (FileManipulator::exists($this->root_dir)) { - if ($this->command->confirm(__('laragine::install.root_dir_exists'), true)) { + if ($this->command->confirm(__('laragine::install.already_installed'), true)) { $allow_publish = true; } else { $allow_publish = false; @@ -25,6 +25,8 @@ public function run() if ($allow_publish) { $this->publishRootDirectory(); + + FileManipulator::generateDir($this->plugins_dir); } } diff --git a/src/Generators/Payloads/Commands/MakeModule.php b/src/Generators/Payloads/Commands/MakeModule.php index 2d7a105..1effc53 100644 --- a/src/Generators/Payloads/Commands/MakeModule.php +++ b/src/Generators/Payloads/Commands/MakeModule.php @@ -7,6 +7,13 @@ class MakeModule extends Base { + /** + * selected directory (root or plugins) + * + * @var string + */ + protected $selected_dir; + /** * run the logic * @@ -14,11 +21,12 @@ class MakeModule extends Base */ public function run() { - $allow_publish = true; - $module_collection = StringManipulator::generate($this->args[0]); - $module_dir = $this->root_dir . '/' . $module_collection['studly']; + $allow_publish = true; + $module_collection = StringManipulator::generate($this->args[0]); + $this->selected_dir = $this->args[1] ? $this->plugins_dir : $this->root_dir; + $module_dir = $this->selected_dir . '/' . $module_collection['studly']; - if (!FileManipulator::exists($this->root_dir)) { + if (!FileManipulator::exists($this->selected_dir)) { $allow_publish = false; $this->command->error(__('laragine::module.run_install')); } @@ -45,9 +53,9 @@ public function run() */ protected function publishModuleDirectory($module_collection) { - $source_dir = __DIR__ . '/../../../Core/Module'; - $destination_dir = $this->root_dir . '/'. $module_collection['studly']; - $files = config('laragine.module.main_files'); + $source_dir = __DIR__ . '/../../../Core/Module'; + $destination_dir = $this->selected_dir . '/'. $module_collection['studly']; + $files = config('laragine.module.main_files'); $search = [ 'file' => ['stub'], @@ -55,7 +63,9 @@ protected function publishModuleDirectory($module_collection) '#UNIT_NAME#', '#UNIT_NAME_PLURAL_LOWER_CASE#', '#UNIT_NAME_LOWER_CASE#', - '#MODULE_NAME#' + '#MODULE_NAME#', + '#SELECTED_DIRECTORY#', + '#IS_PLUGINS#' ] ]; @@ -65,7 +75,9 @@ protected function publishModuleDirectory($module_collection) $module_collection['studly'], $module_collection['plural_lower_case'], $module_collection['singular_lower_case'], - $module_collection['studly'] + $module_collection['studly'], + $this->args[1] ? 'Plugins' : 'Core', + $this->args[1] ? ', true' : '', ] ]; diff --git a/src/Generators/Payloads/Commands/MakeUnit.php b/src/Generators/Payloads/Commands/MakeUnit.php index a8420a8..808985d 100644 --- a/src/Generators/Payloads/Commands/MakeUnit.php +++ b/src/Generators/Payloads/Commands/MakeUnit.php @@ -12,31 +12,38 @@ class MakeUnit extends Base /** * module names (collection) * - * @var + * @var string[] */ public $module_collection; /** * module dir * - * @var + * @var string */ public $module_dir; /** * unit names (collection) * - * @var + * @var string[] */ public $unit_collection; /** * init flag * - * @var + * @var bool */ public $init; + /** + * selected directory (root or plugins) + * + * @var string + */ + protected $selected_dir; + /** * run the logic * @@ -47,11 +54,12 @@ public function run() $this->unit_collection = StringManipulator::generate($this->args[0]); $this->module_collection = StringManipulator::generate($this->args[1]); $this->init = $this->args[2]; - $this->module_dir = $this->root_dir . '/' . $this->module_collection['studly']; + $this->selected_dir = $this->args[3] ? $this->plugins_dir : $this->root_dir; + $this->module_dir = $this->selected_dir . '/' . $this->module_collection['studly']; $validation = new UnitValidation($this->command); $validation->checkModule($this->module_dir) ->checkUnit($this->module_dir, $this->unit_collection, $this->init) - ->checkAttributes($this->root_dir, $this->module_collection, $this->unit_collection); + ->checkAttributes($this->selected_dir, $this->module_collection, $this->unit_collection); if ($validation->allow_proceed) { $this->publishUnit(); @@ -69,12 +77,13 @@ protected function publishUnit() $this->publishUnitInitCase(); } else { $file_name = $this->unit_collection['studly'] . '.json'; - $data = $this->root_dir . '/' . $this->module_collection['studly'] . '/data/' . $file_name; + $data = $this->selected_dir . '/' . $this->module_collection['studly'] . '/data/' . $file_name; $unit_data = [ - 'module_dir' => $this->module_dir, - 'module_collection' => $this->module_collection, - 'unit_collection' => $this->unit_collection + 'module_dir' => $this->module_dir, + 'module_collection' => $this->module_collection, + 'unit_collection' => $this->unit_collection, + 'selected_directory' => $this->args[3] ? 'Plugins' : 'Core', ]; $processors = ['Resource', 'Request', 'Factory', 'Migration']; Factory::create($unit_data, $processors); @@ -89,14 +98,15 @@ protected function publishUnit() */ private function publishUnitInitCase() { $source_dir = __DIR__ . '/../../../Core/Module'; - $destination_dir = $this->root_dir . '/'. $this->module_collection['studly']; + $destination_dir = $this->selected_dir . '/'. $this->module_collection['studly']; $files = config('laragine.module.unit_main_folders'); $search = [ 'file' => ['stub', 'Api', 'Web', 'Unit'], 'content' => [ '#UNIT_NAME#', - '#MODULE_NAME#' + '#MODULE_NAME#', + '#SELECTED_DIRECTORY#', ] ]; @@ -104,7 +114,8 @@ private function publishUnitInitCase() { 'file' => ['php', '', '', $this->unit_collection['studly']], 'content' => [ $this->unit_collection['studly'], - $this->module_collection['studly'] + $this->module_collection['studly'], + $this->args[3] ? 'Plugins' : 'Core', ] ]; diff --git a/src/Logic/FileManipulator.php b/src/Logic/FileManipulator.php index 9040b04..06e4741 100644 --- a/src/Logic/FileManipulator.php +++ b/src/Logic/FileManipulator.php @@ -66,6 +66,17 @@ public static function generate($source_dir, $destination_dir, $files, $search = } } + /** + * generate directory or directories + * + * @param string $destination_dir + * @param integer $mode + */ + public static function generateDir($destination_dir, $mode = 0775) : void + { + File::makeDirectory("$destination_dir", $mode, true, true); + } + protected static function deleteFilesWithMatchSpecificPrefix($destination, $prefix) { foreach (glob("$destination".'/*') as $migration_file) { if(strpos($migration_file, $prefix) !== false) { diff --git a/src/Logic/StringManipulator.php b/src/Logic/StringManipulator.php index 111db4f..60b3ebf 100644 --- a/src/Logic/StringManipulator.php +++ b/src/Logic/StringManipulator.php @@ -10,6 +10,7 @@ class StringManipulator * get all possible shapes of a string * * @param string $string + * @return string[] */ static public function generate(string $string): array { diff --git a/src/Processors/Factory.php b/src/Processors/Factory.php index 8350aad..33b3735 100644 --- a/src/Processors/Factory.php +++ b/src/Processors/Factory.php @@ -46,6 +46,7 @@ public static function create($units_data, array $processors = []) "#REQUEST_STR#", "#FACTORY_STR#", "#MIGRATION_STR#", + '#SELECTED_DIRECTORY#', ] ]; $unit_plural_lower = $units_data['unit_collection']['plural_lower_case']; @@ -60,7 +61,8 @@ public static function create($units_data, array $processors = []) $data['resource_str'], $data['request_str'], $data['factory_str'], - $data['migration_str'] + $data['migration_str'], + $units_data['selected_directory'] ] ]; diff --git a/src/config.php b/src/config.php index a92f476..5648fd9 100644 --- a/src/config.php +++ b/src/config.php @@ -13,6 +13,17 @@ 'root_dir' => base_path() . '/core', + /* + |-------------------------------------------------------------------------- + | The Plugins Directory + |-------------------------------------------------------------------------- + | + | The plugins directory that will be used to push the modules to + | + */ + + 'plugins_dir' => base_path() . '/plugins', + /* |-------------------------------------------------------------------------- | The Base diff --git a/src/lang/en/install.php b/src/lang/en/install.php index 6245404..cb5b749 100644 --- a/src/lang/en/install.php +++ b/src/lang/en/install.php @@ -11,7 +11,7 @@ | */ - 'root_dir_exists' => 'The root directory already exists, do you want to override it?', + 'already_installed' => 'The root directory and the plugins direcotory already exist, do you want to override both directories?', 'root_dir_not_overwritten' => 'Existing root directory was not overwritten', 'success' => 'The installation done successfully!' diff --git a/src/lang/en/module.php b/src/lang/en/module.php index 6bf08dc..cfc8ea1 100644 --- a/src/lang/en/module.php +++ b/src/lang/en/module.php @@ -11,8 +11,8 @@ | */ - 'run_install' => 'Please run install command first', - 'exists' => 'the module directory already exists, do you want to override it?', + 'run_install' => 'Please run the install command first', + 'exists' => 'The module directory already exists, do you want to override it?', 'not_overwritten' => 'Existing module directory was not overwritten', 'success' => 'Module created successfully!', diff --git a/tests/Unit/InstallTest.php b/tests/Unit/InstallTest.php index 57ac555..c7cdebe 100644 --- a/tests/Unit/InstallTest.php +++ b/tests/Unit/InstallTest.php @@ -22,23 +22,23 @@ public function test_the_install_command_create_root_directory() $this->assertTrue(FileManipulator::exists($this->root_dir)); } - public function test_when_root_directory_exists_users_can_choose_to_override_it() + public function test_when_the_package_already_installed_users_can_choose_to_override_the_files() { $command = $this->artisan('laragine:install'); $command->expectsConfirmation( - __('laragine::install.root_dir_exists'), 'yes' + __('laragine::install.already_installed'), 'yes' ); $command->expectsOutput(__('laragine::install.success')); } - public function test_when_root_directory_exists_users_can_choose_to_not_override_it() + public function test_when_the_package_already_installed_users_can_choose_to_not_override_the_files() { $command = $this->artisan('laragine:install'); $command->expectsConfirmation( - __('laragine::install.root_dir_exists'), 'no' + __('laragine::install.already_installed'), 'no' ); $command->expectsOutput(__('laragine::install.root_dir_not_overwritten'));