From e8e53c124f10cc8b44d26364e5bfcccfac252ed3 Mon Sep 17 00:00:00 2001 From: nesciotes Date: Mon, 29 Jun 2020 20:57:49 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + LICENSE.txt | 0 README.md | 122 +++++++++++ composer.json | 53 +++++ config/csr.php | 12 ++ src/Commands/CreateCsr.php | 189 ++++++++++++++++++ src/Commands/stubs/abstract/irepository.stub | 10 + src/Commands/stubs/abstract/iservice.stub | 10 + src/Commands/stubs/concrete/controller.stub | 24 +++ src/Commands/stubs/concrete/model.stub | 21 ++ src/Commands/stubs/concrete/repository.stub | 12 ++ src/Commands/stubs/concrete/service.stub | 24 +++ src/Commands/subcommands/CreateController.php | 37 ++++ .../subcommands/CreateIRepository.php | 37 ++++ src/Commands/subcommands/CreateIService.php | 37 ++++ src/Commands/subcommands/CreateModel.php | 52 +++++ src/Commands/subcommands/CreateRepository.php | 37 ++++ src/Commands/subcommands/CreateService.php | 37 ++++ .../subcommands/CsrGeneratorCommand.php | 104 ++++++++++ src/CsrServiceProvider.php | 63 ++++++ 20 files changed, 884 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 composer.json create mode 100644 config/csr.php create mode 100644 src/Commands/CreateCsr.php create mode 100644 src/Commands/stubs/abstract/irepository.stub create mode 100644 src/Commands/stubs/abstract/iservice.stub create mode 100644 src/Commands/stubs/concrete/controller.stub create mode 100644 src/Commands/stubs/concrete/model.stub create mode 100644 src/Commands/stubs/concrete/repository.stub create mode 100644 src/Commands/stubs/concrete/service.stub create mode 100644 src/Commands/subcommands/CreateController.php create mode 100644 src/Commands/subcommands/CreateIRepository.php create mode 100644 src/Commands/subcommands/CreateIService.php create mode 100644 src/Commands/subcommands/CreateModel.php create mode 100644 src/Commands/subcommands/CreateRepository.php create mode 100644 src/Commands/subcommands/CreateService.php create mode 100644 src/Commands/subcommands/CsrGeneratorCommand.php create mode 100644 src/CsrServiceProvider.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75b2b4e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +vendor +.idea/ +composer.lock diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..900836a --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +# Laravel CSR +CSR stands for Controller/Service/Repository. This package lets you set all that up with just a single command, with the additional options to disable one of the three layers or even generate a model and/or migration automatically aswell. + +## Usage +### Installation + +Install the package using composer: +``` +composer require scrumble-nl/laravel-csr +``` + +Publish the configuration file for default class paths: +``` +php artisan vendor:publish --tag=laravel-csr +``` + +It is possible to modify the default class paths in this newly created `csr.php` config file. + +### Command usage + +The base command is `php artisan csr:gen {name} {namespace (optional)}`. + + +This will generate a controller, service interface, service, repository interface and repository all at once. They automatically are dependency injected into eachother so they are ready for usage immediately. Nice! + +Finally, you need to add the generated service and/or repository to your `AppServiceProvider.php`. + + +#### Example + +`php artisan csr:gen picture holiday` +will generate the following files: + +- `app/Http/Controllers/Holiday/PictureController.php` +- `app/Interfaces/Services/Holiday/IPictureService.php` +- `app/Services/Holiday/PictureService` +- `app/Interfaces/Repositories/Holiday/IPictureRepository` +- `app/Repositories/Holiday/PictureRepository` + +**Note**: The command will automatically capitalize the first character, so `picture` will become `Picture`. If you want your classes to have a name like `PictureBook` you will have to type this correctly yourself. + +Now you will need to register your service and repository: + +AppServiceProvider.php: +```php +bind(IPictureService::class, PictureService::class); + app()->bind(IPictureRepository::class, PictureRepository::class); + } +} + +``` + +And it's done! + +#### Example output + + +app/Http/Controllers/Holiday/PictureController.php: +```php +pictureService = $pictureService; + } +} +``` + +### Command options + +The following options can be used to disable some generation, or even add models and migrations to the command: + +| Name | Type | Required | Description | Default | +|--------------|:----------------------------------------------------------------------------------------:|:----------:|:-------------------------------------| -------- | +| `name` | string | *true* | The base name for all items to generate | | +| `namespace` | string | *false* | The namespace items will be in | +| `--model` | | *false* | Automatically create a model aswell | `false` +| `--migration` | | *false* | Automatically create a migration aswell | `false` +| `--nc` | | *false* | Do not generate the controller | `false` +| `--ns` | | *false* | Do not generate the service and service interface | `false` +| `--nr` | | *false* | Do not generate the repository and repository interface | `false` + +See also: `php artisan csr:gen --help` + +## Contributing +If you would like to see additions/changes to this package you are always welcome to add some code or improve it. + +## Scrumble +This product has been originally developed by [Scrumble](https://www.scrumble.nl) for internal use. As we have been using lots of open source packages we wanted to give back to the community. We hope this helps you getting forward as much as other people helped us! diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..bcb1567 --- /dev/null +++ b/composer.json @@ -0,0 +1,53 @@ +{ + "name": "scrumble-nl/laravel-csr", + "type": "library", + "homepage": "https://github.com/scrumble-nl/laravel-csr", + "description": "This package makes it possible to generate a controller, service, repository, model and migration all in 1 command", + "keywords": [ + "command", + "generate", + "CSR", + "controller", + "service", + "repository", + "model", + "migration" + ], + "license": "MIT", + "authors": [ + { + "name": "Rico Clark", + "email": "rico@scrumble.nl" + }, + { + "name": "Luuk de Weijer", + "email": "luuk@scrumble.nl" + } + ], + "autoload": { + "psr-4": {"Scrumble\\Csr\\": "src/"}, + "classmap": [ + "src" + ] + }, + "autoload-dev": { + "classmap": [ + ] + }, + "require": { + "php": ">=7.3.1", + "laravel/framework": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "replace": { + }, + "extra": { + "laravel": { + "providers": [ + "Scrumble\\Csr\\Src\\CsrServiceProvider" + ] + } + } +} diff --git a/config/csr.php b/config/csr.php new file mode 100644 index 0000000..3a789b1 --- /dev/null +++ b/config/csr.php @@ -0,0 +1,12 @@ + [ + 'model' => 'Models', + 'controller' => 'Http/Controllers', + 'service' => 'Services', + 'service_interface' => 'Interfaces/Services', + 'repository' => 'Repositories', + 'repository_interface' => 'Interfaces/Repositories', + ], +]; diff --git a/src/Commands/CreateCsr.php b/src/Commands/CreateCsr.php new file mode 100644 index 0000000..3a4b5be --- /dev/null +++ b/src/Commands/CreateCsr.php @@ -0,0 +1,189 @@ +argument('name')); + $namespace = ucfirst($this->argument('namespace') ?? ''); + + $options = [ + 'controller' => !$this->option('nc'), + 'service' => !$this->option('ns'), + 'repository' => !$this->option('nr'), + 'model' => $this->option('model'), + 'migration' => $this->option('migration'), + ]; + + $enabledOptions = array_filter($options, function ($option) { + return !!$option; + }); + + if (!$this->confirm('Are you sure you want to create: [' + . implode(', ', array_keys($enabledOptions)) + . '] with the NAME: ' . $name + . ($namespace ? ' and NAMESPACE: ' . $namespace : '') . '?')) { + $this->warn('Cancelled'); + return; + } + + if (!$this->option('nc')) { + $this->createController($namespace, $name); + } + + if (!$this->option('ns')) { + $this->createService($namespace, $name); + } + + if (!$this->option('nr')) { + $this->createRepository($namespace, $name); + } + + if ($this->option('model')) { + $this->createModel($namespace, $name); + } + + if ($this->option('migration')) { + $this->createMigration(); + } + + $this->alert('Generation complete, have fun doing some actual programming!'); + + if (!$this->option('ns') || !$this->option('nr')) { + $this->info('But first, there\'s one more thing to do:'); + + if (!$this->option('ns')) { + $this->warn('> Don\'t forget to bind the service in the AppServiceProvider'); + } + + if (!$this->option('nr')) { + $this->warn('> Don\'t forget to bind the repository in the AppServiceProvider'); + } + } + } + + /** + * Create the controller + * + * @param string $namespace + * @param string $name + */ + private function createController(string $namespace, string $name): void + { + $this->call('csr:controller', [ + 'name' => config('csr.paths.controller') . '/' . $namespace . '/' . $name . 'Controller', + 'basename' => $name, + 'namespace' => $namespace, + ]); + } + + /** + * Create the service + * + * @param string $namespace + * @param string $name + */ + private function createService(string $namespace, string $name): void + { + $this->call('csr:iservice', [ + 'name' => config('csr.paths.service_interface') . '/' . $namespace . '/I' . $name . 'Service', + 'basename' => $name, + 'namespace' => $namespace, + ]); + + $asdf = $this->call('csr:service', [ + 'name' => config('csr.paths.service') . '/' . $namespace . '/' . $name . 'Service', + 'basename' => $name, + 'namespace' => $namespace, + ]); + } + + /** + * Create the repository + * + * @param string $namespace + * @param string $name + */ + private function createRepository(string $namespace, string $name): void + { + $this->call('csr:irepository', [ + 'name' => config('csr.paths.repository_interface') . '/' . $namespace . '/I' . $name . 'Repository', + 'basename' => $name, + 'namespace' => $namespace, + ]); + + $this->call('csr:repository', [ + 'name' => config('csr.paths.repository') . '/' . $namespace . '/' . $name . 'Repository', + 'basename' => $name, + 'namespace' => $namespace, + ]); + } + + /** + * Create the model + * + * @param string $namespace + * @param string $name + */ + private function createModel(string $namespace, string $name): void + { + $this->call('csr:model', [ + 'name' => config('csr.paths.model') . '/' . $namespace . '/' . $name, + 'namespace' => $namespace, + ]); + } + + /** + * Create the migration + */ + private function createMigration(): void + { + $table = Str::singular(Str::snake(class_basename($this->argument('name')))); + + $this->call('make:migration', [ + 'name' => "create_{$table}_table", + '--create' => $table, + ]); + } +} diff --git a/src/Commands/stubs/abstract/irepository.stub b/src/Commands/stubs/abstract/irepository.stub new file mode 100644 index 0000000..2085d56 --- /dev/null +++ b/src/Commands/stubs/abstract/irepository.stub @@ -0,0 +1,10 @@ +{{baseName}}Service = ${{baseName}}Service; + } +} diff --git a/src/Commands/stubs/concrete/model.stub b/src/Commands/stubs/concrete/model.stub new file mode 100644 index 0000000..3be8e96 --- /dev/null +++ b/src/Commands/stubs/concrete/model.stub @@ -0,0 +1,21 @@ +{{baseName}}Repository = ${{baseName}}Repository; + } +} diff --git a/src/Commands/subcommands/CreateController.php b/src/Commands/subcommands/CreateController.php new file mode 100644 index 0000000..9c8c075 --- /dev/null +++ b/src/Commands/subcommands/CreateController.php @@ -0,0 +1,37 @@ +validateName($this->argument('name')); + + return array_merge($replace, [ + 'DummyClass' => class_basename($className), + '{{variable}}' => lcfirst(class_basename($className)), + ]); + } +} diff --git a/src/Commands/subcommands/CreateRepository.php b/src/Commands/subcommands/CreateRepository.php new file mode 100644 index 0000000..4627d85 --- /dev/null +++ b/src/Commands/subcommands/CreateRepository.php @@ -0,0 +1,37 @@ +buildReplacements($replace); + + return str_replace( + array_keys($replace), array_values($replace), parent::buildClass($name) + ); + } + + /** + * Build the controller replacement values. + * + * @param array $replace + * @return array + */ + protected function buildReplacements(array $replace) + { + $className = $this->validateName($this->argument('name')); + $namespace = $this->argument('namespace'); + $baseName = $this->argument('basename'); + + return array_merge($replace, [ + '{{BaseName}}' => $baseName, + '{{baseName}}' => lcfirst($baseName), + '{{Variable}}' => ucfirst(class_basename($className)), + '{{NamespaceShort}}' => $namespace ? ucfirst(strtolower($namespace)) . '\\' : '', + ]); + } + + /** + * Validate the given name and return + * the full classname + * + * @param string $name + * @return string + * + * @throws \InvalidArgumentException + */ + protected function validateName($name) + { + if (preg_match('([^A-Za-z0-9_/\\\\])', $name)) { + throw new InvalidArgumentException('The name "' . $name . '" contains invalid characters.'); + } + + $name = trim(str_replace('/', '\\', $name), '\\'); + + if (!Str::startsWith($name, $rootNamespace = $this->laravel->getNamespace())) { + $name = $rootNamespace . $name; + } + + return $name; + } +} diff --git a/src/CsrServiceProvider.php b/src/CsrServiceProvider.php new file mode 100644 index 0000000..90a5ca4 --- /dev/null +++ b/src/CsrServiceProvider.php @@ -0,0 +1,63 @@ +handleConfigs(); + } + + /** + * Register any application services. + * + * @return void + */ + public function register(): void + { + $this->commands([ + CreateCsr::class, + CreateModel::class, + CreateController::class, + CreateIService::class, + CreateService::class, + CreateIRepository::class, + CreateRepository::class, + ]); + } + + /** + * Fetches and publishes config file + * + * @return void + */ + private function handleConfigs(): void + { + $configPath = __DIR__ . '/../config/csr.php'; + $this->publishes([$configPath => config_path('csr.php')], 'laravel-csr'); + } +}