From b673601dcd56a51ae27002670c9502959ecb61c5 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Sat, 6 Apr 2024 15:28:32 +0300 Subject: [PATCH 1/2] New Feature: Generate Models - Can correctly generate a model - Can specify to generate a migration with it - TODO: override the creation of other classes alongside the model once these implementations are in place, e.g factory, controller etc. --- src/Commands/ModelMakeCommand.php | 125 ++++++++++++++++++++++++++++++ src/Module.php | 4 + 2 files changed, 129 insertions(+) create mode 100644 src/Commands/ModelMakeCommand.php diff --git a/src/Commands/ModelMakeCommand.php b/src/Commands/ModelMakeCommand.php new file mode 100644 index 0000000..c40d754 --- /dev/null +++ b/src/Commands/ModelMakeCommand.php @@ -0,0 +1,125 @@ +argument('module')); + } + + protected function getDefaultNamespace($rootNamespace): string + { + return $rootNamespace.'\\Models'; + } + + protected function rootNamespace(): string + { + return $this->getModule()->getRootNamespace(); + } + protected function getPath($name): string + { + $name = Str::replaceFirst($this->rootNamespace(), '', $name); + return $this->getModule()->srcPath(str_replace('\\', '/', $name).'.php'); + } + + protected function createFactory(): void + { + $factory = Str::studly($this->argument('name')); + + $this->call('make:factory', [ + 'name' => "{$factory}Factory", + '--model' => $this->qualifyClass($this->getNameInput()), + ]); + } + + /** + * Create a migration file for the model. + * + * @return void + */ + protected function createMigration(): void + { + $table = Str::snake(Str::pluralStudly(class_basename($this->argument('name')))); + + if ($this->option('pivot')) { + $table = Str::singular($table); + } + + $this->call('modular:make-migration', [ + 'name' => "create_{$table}_table", + 'module' => $this->getModule()->name(), + '--create' => $table, + ]); + } + + /** + * Create a seeder file for the model. + * + * @return void + */ + protected function createSeeder(): void + { + $seeder = Str::studly(class_basename($this->argument('name'))); + + $this->call('make:seeder', [ + 'name' => "{$seeder}Seeder", + ]); + } + + /** + * Create a controller for the model. + * + * @return void + */ + protected function createController(): void + { + $controller = Str::studly(class_basename($this->argument('name'))); + + $modelName = $this->qualifyClass($this->getNameInput()); + + $this->call('make:controller', array_filter([ + 'name' => "{$controller}Controller", + '--model' => $this->option('resource') || $this->option('api') ? $modelName : null, + '--api' => $this->option('api'), + '--requests' => $this->option('requests') || $this->option('all'), + '--test' => $this->option('test'), + '--pest' => $this->option('pest'), + ])); + } + + /** + * Create a policy file for the model. + * + * @return void + */ + protected function createPolicy(): void + { + $policy = Str::studly(class_basename($this->argument('name'))); + + $this->call('make:policy', [ + 'name' => "{$policy}Policy", + '--model' => $this->qualifyClass($this->getNameInput()), + ]); + } +} diff --git a/src/Module.php b/src/Module.php index 9a719b5..aab13d6 100644 --- a/src/Module.php +++ b/src/Module.php @@ -37,6 +37,10 @@ public function path(string $path = '', bool $relative = false): string return $basePath . ($path ? DIRECTORY_SEPARATOR . trim($path, DIRECTORY_SEPARATOR) : ''); } + public function getRootNamespace(): string + { + return $this->namespace.'\\'; + } public function makeNamespace(string $relativeNamespace = ''): string { return $this->namespace . ($relativeNamespace ? '\\' . ltrim($relativeNamespace, '\\') : ''); From e10f84d0879770d4ebff0312bfa0ca938144a718 Mon Sep 17 00:00:00 2001 From: coolsam726 Date: Sat, 6 Apr 2024 12:30:04 +0000 Subject: [PATCH 2/2] Fix styling --- src/Commands/ActivateModuleCommand.php | 6 ++- src/Commands/DeactivateModuleCommand.php | 6 ++- src/Commands/MakeModuleCommand.php | 42 +++++++++++++-------- src/Commands/MigrationMakeCommand.php | 11 +++--- src/Commands/ModelMakeCommand.php | 12 ++---- src/ModularServiceProvider.php | 17 +++++---- src/Module.php | 33 +++++++++------- src/Support/Concerns/CanManipulateFiles.php | 14 +++---- 8 files changed, 79 insertions(+), 62 deletions(-) diff --git a/src/Commands/ActivateModuleCommand.php b/src/Commands/ActivateModuleCommand.php index c52903c..ceed144 100644 --- a/src/Commands/ActivateModuleCommand.php +++ b/src/Commands/ActivateModuleCommand.php @@ -5,13 +5,15 @@ use Illuminate\Console\Command; use Illuminate\Support\Str; use Savannabits\Modular\Facades\Modular; + use function Laravel\Prompts\text; class ActivateModuleCommand extends Command { - public $signature = 'modular:activate {name?}'; + public $description = 'Activate a module'; + private string $moduleName; public function handle(): void @@ -24,7 +26,7 @@ public function handle(): void private function activateModule(): void { $moduleName = $this->moduleName; - $repoName = config('modular.vendor','modular').'/'.$moduleName; + $repoName = config('modular.vendor', 'modular').'/'.$moduleName; Modular::execCommand('composer require '.$repoName.':@dev'); Modular::execCommand("php artisan $moduleName:install"); } diff --git a/src/Commands/DeactivateModuleCommand.php b/src/Commands/DeactivateModuleCommand.php index 7e85782..11fd989 100644 --- a/src/Commands/DeactivateModuleCommand.php +++ b/src/Commands/DeactivateModuleCommand.php @@ -5,13 +5,15 @@ use Illuminate\Console\Command; use Illuminate\Support\Str; use Savannabits\Modular\Facades\Modular; + use function Laravel\Prompts\text; class DeactivateModuleCommand extends Command { - public $signature = 'modular:deactivate {name?}'; + public $description = 'Deactivate a module'; + private string $moduleName; public function handle(): void @@ -24,7 +26,7 @@ public function handle(): void private function deactivateModule(): void { $moduleName = $this->moduleName; - $repoName = config('modular.vendor','modular').'/'.$moduleName; + $repoName = config('modular.vendor', 'modular').'/'.$moduleName; Modular::execCommand("composer remove $repoName"); } } diff --git a/src/Commands/MakeModuleCommand.php b/src/Commands/MakeModuleCommand.php index a026c7c..ecd04e0 100644 --- a/src/Commands/MakeModuleCommand.php +++ b/src/Commands/MakeModuleCommand.php @@ -8,17 +8,25 @@ use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Savannabits\Modular\Support\Concerns\CanManipulateFiles; + use function Laravel\Prompts\text; class MakeModuleCommand extends Command { use CanManipulateFiles; + public $signature = 'modular:make {name?} {--F|force}'; + public $description = 'Create a new module'; + private string $moduleName; + private string $moduleTitle; + private string $moduleNamespace; + private string $modulePath; + private string $moduleStudlyName; public function handle() @@ -27,8 +35,8 @@ public function handle() $this->moduleName = Str::of($name)->kebab()->toString(); $this->moduleStudlyName = Str::of($name)->studly()->toString(); $this->moduleTitle = Str::of($name)->kebab()->title()->replace('-', ' ')->toString(); - $this->moduleNamespace = config('modular.namespace') . '\\' . Str::of($name)->studly()->toString(); - $this->modulePath = config('modular.path') . '/' . $this->moduleName; + $this->moduleNamespace = config('modular.namespace').'\\'.Str::of($name)->studly()->toString(); + $this->modulePath = config('modular.path').'/'.$this->moduleName; $this->info("Creating module: $this->moduleName in $this->modulePath"); $this->generateModuleDirectories(); @@ -39,18 +47,20 @@ public function handle() private function generateModuleDirectories(): bool { $directories = config('modular.directory_tree'); - if (!count($directories)) { + if (! count($directories)) { $this->error('No directories found in the configuration file'); + return false; } foreach ($directories as $directory) { - $path = $this->modulePath . '/' . ltrim($directory,'/'); - if (!is_dir($path)) { + $path = $this->modulePath.'/'.ltrim($directory, '/'); + if (! is_dir($path)) { mkdir($path, 0775, true); $this->info("Created directory: $path"); } } $this->info('Module directories created successfully'); + return true; } @@ -62,28 +72,28 @@ private function generateModuleFiles(): void } catch (FileNotFoundException|NotFoundExceptionInterface|ContainerExceptionInterface $e) { $this->error($e->getMessage()); } -// $this->generateModuleFacade(); + // $this->generateModuleFacade(); } private function generateModuleComposerFile(): void { $composerJson = [ - 'name' => config('modular.vendor') . '/' . $this->moduleName, + 'name' => config('modular.vendor').'/'.$this->moduleName, 'type' => 'library', - 'description' => $this->moduleTitle . ' module', + 'description' => $this->moduleTitle.' module', 'require' => [ 'php' => '^8.2', ], 'autoload' => [ 'psr-4' => [ - $this->moduleNamespace . '\\' => 'src/', - $this->moduleNamespace . '\\Database\\Factories\\' => 'database/factories/', - $this->moduleNamespace . '\\Database\\Seeders\\' => 'database/seeders/', + $this->moduleNamespace.'\\' => 'src/', + $this->moduleNamespace.'\\Database\\Factories\\' => 'database/factories/', + $this->moduleNamespace.'\\Database\\Seeders\\' => 'database/seeders/', ], ], 'autoload-dev' => [ 'psr-4' => [ - $this->moduleNamespace . '\\Tests\\' => 'tests/', + $this->moduleNamespace.'\\Tests\\' => 'tests/', ], ], 'config' => [ @@ -96,12 +106,12 @@ private function generateModuleComposerFile(): void 'extra' => [ 'laravel' => [ 'providers' => [ - $this->moduleNamespace . '\\' . $this->moduleStudlyName . 'ServiceProvider', + $this->moduleNamespace.'\\'.$this->moduleStudlyName.'ServiceProvider', ], ], ], ]; - $composerJsonPath = $this->modulePath . '/composer.json'; + $composerJsonPath = $this->modulePath.'/composer.json'; file_put_contents($composerJsonPath, json_encode($composerJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); $this->info("Created composer.json file: $composerJsonPath"); } @@ -113,9 +123,9 @@ private function generateModuleComposerFile(): void */ private function generateModuleServiceProvider(): void { - $path = $this->modulePath . '/src/' . $this->moduleStudlyName . 'ServiceProvider.php'; + $path = $this->modulePath.'/src/'.$this->moduleStudlyName.'ServiceProvider.php'; $namespace = $this->moduleNamespace; - $class = $this->moduleStudlyName . 'ServiceProvider'; + $class = $this->moduleStudlyName.'ServiceProvider'; $this->copyStubToApp('module.provider', $path, [ 'class' => $class, 'namespace' => $namespace, diff --git a/src/Commands/MigrationMakeCommand.php b/src/Commands/MigrationMakeCommand.php index b9c17de..e7c1774 100644 --- a/src/Commands/MigrationMakeCommand.php +++ b/src/Commands/MigrationMakeCommand.php @@ -1,12 +1,10 @@ argument('module'))->migrationsPath(relative: true); + return Modular::module($this->argument('module'))->migrationsPath(relative: true); } protected function promptForMissingArgumentsUsing(): array { return [ - 'name' => ['What should the migration name be?','e.g create_planets_table'], - 'module' => ['Enter the module name (It has to be existing)','e.g Blog, BlogPost, blog-post'] + 'name' => ['What should the migration name be?', 'e.g create_planets_table'], + 'module' => ['Enter the module name (It has to be existing)', 'e.g Blog, BlogPost, blog-post'], ]; } } diff --git a/src/Commands/ModelMakeCommand.php b/src/Commands/ModelMakeCommand.php index c40d754..a3f038f 100644 --- a/src/Commands/ModelMakeCommand.php +++ b/src/Commands/ModelMakeCommand.php @@ -18,7 +18,7 @@ class ModelMakeCommand extends BaseModelMakeCommand protected function getArguments(): array { - return array_merge(parent::getArguments(),[ + return array_merge(parent::getArguments(), [ ['module', InputArgument::REQUIRED, 'The name of the module in which this should be installed'], ]); } @@ -37,9 +37,11 @@ protected function rootNamespace(): string { return $this->getModule()->getRootNamespace(); } + protected function getPath($name): string { $name = Str::replaceFirst($this->rootNamespace(), '', $name); + return $this->getModule()->srcPath(str_replace('\\', '/', $name).'.php'); } @@ -55,8 +57,6 @@ protected function createFactory(): void /** * Create a migration file for the model. - * - * @return void */ protected function createMigration(): void { @@ -75,8 +75,6 @@ protected function createMigration(): void /** * Create a seeder file for the model. - * - * @return void */ protected function createSeeder(): void { @@ -89,8 +87,6 @@ protected function createSeeder(): void /** * Create a controller for the model. - * - * @return void */ protected function createController(): void { @@ -110,8 +106,6 @@ protected function createController(): void /** * Create a policy file for the model. - * - * @return void */ protected function createPolicy(): void { diff --git a/src/ModularServiceProvider.php b/src/ModularServiceProvider.php index a0a5c44..dfb110c 100644 --- a/src/ModularServiceProvider.php +++ b/src/ModularServiceProvider.php @@ -7,11 +7,15 @@ use Spatie\LaravelPackageTools\Commands\InstallCommand; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; + class ModularServiceProvider extends PackageServiceProvider { public static string $name = 'modular'; + public static string $vendor = 'savannabits'; + public static string $viewNamespace = 'modular'; + public function configurePackage(Package $package): void { /* @@ -30,8 +34,7 @@ public function configurePackage(Package $package): void $command ->askToStarRepoOnGitHub($name) ->startWith(fn (InstallCommand $command) => $this->installationSteps($command)); - }) - ; + }); $this->mergeConfigFrom($this->package->basePath('/../config/modular.php'), 'modular'); } @@ -40,10 +43,10 @@ private function configureComposerMerge(InstallCommand $command): void $command->comment('Configuring Composer merge plugin:'); $composerJson = json_decode(file_get_contents(base_path('composer.json')), true); // Add the modules repositories into compose if they don't exist - if (!isset($composerJson['repositories'])){ + if (! isset($composerJson['repositories'])) { $composerJson['repositories'] = []; } - if (!collect($composerJson['repositories'])->contains(fn ($repo) => $repo['type'] === 'path' && $repo['url'] === config('modular.path').'/*')) { + if (! collect($composerJson['repositories'])->contains(fn ($repo) => $repo['type'] === 'path' && $repo['url'] === config('modular.path').'/*')) { $composerJson['repositories'][] = [ 'type' => 'path', 'url' => config('modular.path').'/*', @@ -52,7 +55,7 @@ private function configureComposerMerge(InstallCommand $command): void ], ]; } - if (!isset($composerJson['extra']['merge-plugin'])) { + if (! isset($composerJson['extra']['merge-plugin'])) { $composerJson['extra']['merge-plugin'] = [ 'include' => [ 'modules/*/composer.json', @@ -65,7 +68,7 @@ private function configureComposerMerge(InstallCommand $command): void ]; // Ensure the composer-merge-plugin is in the list of allowed plugins - if (!isset($composerJson['config']['allow-plugins'])) { + if (! isset($composerJson['config']['allow-plugins'])) { $composerJson['config']['allow-plugins'] = []; } // If allowed-plugins is set to true, disregard @@ -93,7 +96,7 @@ private function ensureModularPathExists(InstallCommand $command): void { $command->comment('Ensuring modular path exists:'); $path = config('modular.path'); - if (!is_dir($path)) { + if (! is_dir($path)) { mkdir($path, 0755, true); $command->info("Directory $path created successfully"); } else { diff --git a/src/Module.php b/src/Module.php index aab13d6..08ed944 100644 --- a/src/Module.php +++ b/src/Module.php @@ -7,9 +7,13 @@ class Module { private string $name; + private string $title; + private string $studlyName; + private string $namespace; + private string $basePath; /** @@ -20,30 +24,32 @@ public function __construct(string $name) $this->name = Str::kebab($name); $this->title = Str::of($name)->kebab()->title()->replace('-', ' ')->toString(); // If the module does not exist, throw an error - if (!is_dir(app()->basePath(config('modular.path') . DIRECTORY_SEPARATOR . $this->name))) { - abort(404,"Module $name does not exist"); + if (! is_dir(app()->basePath(config('modular.path').DIRECTORY_SEPARATOR.$this->name))) { + abort(404, "Module $name does not exist"); } $this->studlyName = Str::of($name)->studly()->toString(); - $this->namespace = config('modular.namespace') . '\\' . $this->studlyName; - $this->basePath = app()->basePath(config('modular.path') . DIRECTORY_SEPARATOR . trim($this->name, DIRECTORY_SEPARATOR)); + $this->namespace = config('modular.namespace').'\\'.$this->studlyName; + $this->basePath = app()->basePath(config('modular.path').DIRECTORY_SEPARATOR.trim($this->name, DIRECTORY_SEPARATOR)); } public function path(string $path = '', bool $relative = false): string { $basePath = $this->basePath; if ($relative) { - $basePath = config('modular.path') . DIRECTORY_SEPARATOR . trim($this->name, DIRECTORY_SEPARATOR); + $basePath = config('modular.path').DIRECTORY_SEPARATOR.trim($this->name, DIRECTORY_SEPARATOR); } - return $basePath . ($path ? DIRECTORY_SEPARATOR . trim($path, DIRECTORY_SEPARATOR) : ''); + + return $basePath.($path ? DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR) : ''); } public function getRootNamespace(): string { return $this->namespace.'\\'; } + public function makeNamespace(string $relativeNamespace = ''): string { - return $this->namespace . ($relativeNamespace ? '\\' . ltrim($relativeNamespace, '\\') : ''); + return $this->namespace.($relativeNamespace ? '\\'.ltrim($relativeNamespace, '\\') : ''); } public function name(): string @@ -73,30 +79,31 @@ public function __toString(): string public function configPath(string $path = '', bool $relative = false): string { - return $this->path('config'.DIRECTORY_SEPARATOR. ltrim($path,DIRECTORY_SEPARATOR), relative: $relative); + return $this->path('config'.DIRECTORY_SEPARATOR.ltrim($path, DIRECTORY_SEPARATOR), relative: $relative); } public function databasePath(string $path = '', bool $relative = false): string { - return $this->path('database'.DIRECTORY_SEPARATOR. trim($path,DIRECTORY_SEPARATOR), relative: $relative); + return $this->path('database'.DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR), relative: $relative); } public function migrationsPath(string $path = '', bool $relative = false): string { - return $this->databasePath('migrations'.DIRECTORY_SEPARATOR. trim($path,DIRECTORY_SEPARATOR),relative: $relative); + return $this->databasePath('migrations'.DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR), relative: $relative); } + public function seedsPath(string $path = '', bool $relative = false): string { - return $this->databasePath('seeds'.DIRECTORY_SEPARATOR. trim($path,DIRECTORY_SEPARATOR), relative: $relative); + return $this->databasePath('seeds'.DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR), relative: $relative); } public function factoriesPath(string $path = '', bool $relative = false): string { - return $this->databasePath('factories'.DIRECTORY_SEPARATOR. trim($path,DIRECTORY_SEPARATOR), $relative); + return $this->databasePath('factories'.DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR), $relative); } public function srcPath(string $path = '', bool $relative = false): string { - return $this->path('src'.DIRECTORY_SEPARATOR. trim($path,DIRECTORY_SEPARATOR), $relative); + return $this->path('src'.DIRECTORY_SEPARATOR.trim($path, DIRECTORY_SEPARATOR), $relative); } } diff --git a/src/Support/Concerns/CanManipulateFiles.php b/src/Support/Concerns/CanManipulateFiles.php index c6eb0aa..fe9f04f 100644 --- a/src/Support/Concerns/CanManipulateFiles.php +++ b/src/Support/Concerns/CanManipulateFiles.php @@ -17,16 +17,16 @@ trait CanManipulateFiles { /** - * @param array $paths + * @param array $paths */ protected function checkForCollision(array $paths): bool { foreach ($paths as $path) { - if (!$this->fileExists($path)) { + if (! $this->fileExists($path)) { continue; } - if (!confirm(basename($path) . ' already exists, do you want to overwrite it?')) { + if (! confirm(basename($path).' already exists, do you want to overwrite it?')) { $this->components->error("{$path} already exists, aborting."); return true; @@ -39,7 +39,7 @@ protected function checkForCollision(array $paths): bool } /** - * @param array $replacements + * @param array $replacements * * @throws FileNotFoundException * @throws ContainerExceptionInterface @@ -50,7 +50,7 @@ protected function copyStubToApp(string $stub, string $targetPath, array $replac $filesystem = app(Filesystem::class); // Get the correct stub path whether published or not - $stubPath = $this->getDefaultStubPath() . "/{$stub}.stub"; + $stubPath = $this->getDefaultStubPath()."/{$stub}.stub"; $stub = str($filesystem->get($stubPath)); @@ -58,7 +58,7 @@ protected function copyStubToApp(string $stub, string $targetPath, array $replac $stub = $stub->replace("{{ {$key} }}", $replacement); } - $stub = (string)$stub; + $stub = (string) $stub; $this->writeFile($targetPath, $stub); } @@ -85,7 +85,7 @@ protected function getDefaultStubPath(): string { $reflectionClass = new ReflectionClass($this); - return (string)str($reflectionClass->getFileName()) + return (string) str($reflectionClass->getFileName()) ->beforeLast('Commands') ->append('../stubs'); }