From 108596431a9a749747f2701e4836eed2a35050da Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Thu, 29 Mar 2018 06:37:32 +0800 Subject: [PATCH] Create new command make:crud-api Generate Api Controller --- src/CrudApiMake.php | 113 +++++++++++++++ src/Generators/ControllerGenerator.php | 19 ++- src/ServiceProvider.php | 1 + src/stubs/controller.api.stub | 88 ++++++++++++ tests/CrudApiMakeCommandTest.php | 156 +++++++++++++++++++++ .../Generators/Api/ApiControllerGeneratorTest.php | 106 ++++++++++++++ 6 files changed, 481 insertions(+), 2 deletions(-) create mode 100644 src/CrudApiMake.php create mode 100644 src/stubs/controller.api.stub create mode 100644 tests/CrudApiMakeCommandTest.php create mode 100644 tests/Generators/Api/ApiControllerGeneratorTest.php diff --git a/src/CrudApiMake.php b/src/CrudApiMake.php new file mode 100644 index 0000000..0dbf6cf --- /dev/null +++ b/src/CrudApiMake.php @@ -0,0 +1,113 @@ +getModelName(); + + if ($this->modelExists()) { + $this->error("{$this->modelNames['model_name']} model already exists."); + return; + } + + // Warn if it has no default layout view based on + // simple-crud.default_layout_view config + if ($this->defaultLayoutNotExists()) { + $this->warn(config('simple-crud.default_layout_view').' view does not exists.'); + } + + if ($this->option('tests-only')) { + $this->generateTestFiles(); + + $this->info('Test files generated successfully!'); + return; + } + + $this->generateRoutes(); + $this->generateModel(); + $this->generateController(); + $this->generateResources(); + $this->generateTestFiles(); + + $this->info('CRUD files generated successfully!'); + } + + /** + * Generate test files + * + * @return void + */ + public function generateTestFiles() + { + app('Luthfi\CrudGenerator\Generators\ModelTestGenerator', ['command' => $this])->generate(); + app('Luthfi\CrudGenerator\Generators\FeatureTestGenerator', ['command' => $this])->generate('simple'); + app('Luthfi\CrudGenerator\Generators\ModelPolicyTestGenerator', ['command' => $this])->generate(); + } + + /** + * Generate Controller + * + * @return void + */ + public function generateController() + { + app('Luthfi\CrudGenerator\Generators\ControllerGenerator', ['command' => $this])->generate('api'); + } + + /** + * Generate Model + * + * @return void + */ + public function generateModel() + { + app('Luthfi\CrudGenerator\Generators\ModelGenerator', ['command' => $this])->generate(); + app('Luthfi\CrudGenerator\Generators\MigrationGenerator', ['command' => $this])->generate(); + app('Luthfi\CrudGenerator\Generators\ModelPolicyGenerator', ['command' => $this])->generate(); + app('Luthfi\CrudGenerator\Generators\ModelFactoryGenerator', ['command' => $this])->generate(); + } + + /** + * Generate Route Route + * + * @return void + */ + public function generateRoutes() + { + app('Luthfi\CrudGenerator\Generators\WebRouteGenerator', ['command' => $this])->generate(); + } + + /** + * Generate Resources + * + * @return void + */ + public function generateResources() + { + app('Luthfi\CrudGenerator\Generators\LangFileGenerator', ['command' => $this])->generate(); + app('Luthfi\CrudGenerator\Generators\FormViewGenerator', ['command' => $this])->generate('simple'); + app('Luthfi\CrudGenerator\Generators\IndexViewGenerator', ['command' => $this])->generate('simple'); + } +} diff --git a/src/Generators/ControllerGenerator.php b/src/Generators/ControllerGenerator.php index 7940311..3152ea7 100644 --- a/src/Generators/ControllerGenerator.php +++ b/src/Generators/ControllerGenerator.php @@ -12,16 +12,26 @@ class ControllerGenerator extends BaseGenerator */ public function generate(string $type = 'full') { + $pluralModelName = $this->modelNames['plural_model_name']; $parentControllerDirectory = ''; if (!is_null($this->command->option('parent'))) { $parentControllerDirectory = '/'.$this->command->option('parent'); } + + if ($this->isForApi()) { + $parentControllerDirectory = '/Api'.$parentControllerDirectory; + } + $controllerPath = $this->makeDirectory(app_path('Http/Controllers'.$parentControllerDirectory)); + $controllerPath = $controllerPath.'/'.$pluralModelName.'Controller.php'; - $controllerPath = $controllerPath.'/'.$this->modelNames['plural_model_name'].'Controller.php'; $this->generateFile($controllerPath, $this->getContent('controller.'.$type)); - $this->command->info($this->modelNames['plural_model_name'].'Controller generated.'); + if ($this->isForApi()) { + $pluralModelName = 'Api/'.$pluralModelName; + } + + $this->command->info($pluralModelName.'Controller generated.'); } /** @@ -61,4 +71,9 @@ class ControllerGenerator extends BaseGenerator return $controllerFileContent; } + + private function isForApi() + { + return $this->command->getName() == 'make:crud-api'; + } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9b4689e..9b78cc8 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -15,6 +15,7 @@ class ServiceProvider extends BaseServiceProvider $this->commands([ CrudMake::class, CrudSimpleMake::class, + CrudApiMake::class, ]); } diff --git a/src/stubs/controller.api.stub b/src/stubs/controller.api.stub new file mode 100644 index 0000000..8e4ca63 --- /dev/null +++ b/src/stubs/controller.api.stub @@ -0,0 +1,88 @@ +where('name', 'like', '%'.request('q').'%'); + })->paginate(25); + + return $mstrCollections; + } + + /** + * Store a newly created singleMstr in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + $this->authorize('create', new Master); + + $this->validate($request, [ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + + $newMaster = $request->only('name', 'description'); + $newMaster['creator_id'] = auth()->id(); + + $singleMstr = Master::create($newMaster); + + return $singleMstr; + } + + /** + * Update the specified singleMstr in storage. + * + * @param \Illuminate\Http\Request $request + * @param \fullMstr $singleMstr + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Master $singleMstr) + { + $this->authorize('update', $singleMstr); + + $this->validate($request, [ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + + $singleMstr->update($request->only('name', 'description')); + + return $singleMstr; + } + + /** + * Remove the specified singleMstr from storage. + * + * @param \fullMstr $singleMstr + * @return \Illuminate\Http\Response + */ + public function destroy(Master $singleMstr) + { + $this->authorize('delete', $singleMstr); + + $this->validate(request(), [ + 'master_id' => 'required', + ]); + + if (request('master_id') == $singleMstr->id && $singleMstr->delete()) { + return response()->json('master deleted.', 204); + } + + return response()->json('Unprocessable Entity.', 422); + } +} diff --git a/tests/CrudApiMakeCommandTest.php b/tests/CrudApiMakeCommandTest.php new file mode 100644 index 0000000..3bcc738 --- /dev/null +++ b/tests/CrudApiMakeCommandTest.php @@ -0,0 +1,156 @@ +artisan('make:crud-api', ['name' => $this->model_name, '--no-interaction' => true]); + + $this->assertNotContains("{$this->model_name} model already exists.", app(Kernel::class)->output()); + + $this->assertFileExists(app_path($this->model_name.'.php')); + $this->assertFileExists(app_path("Http/Controllers/Api/{$this->plural_model_name}Controller.php")); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_'.$this->table_name.'_table.php'); + $this->assertFileExists($migrationFilePath); + + $this->assertFileExists(resource_path("views/{$this->table_name}/index.blade.php")); + $this->assertFileExists(resource_path("views/{$this->table_name}/forms.blade.php")); + + $localeConfig = config('app.locale'); + $this->assertFileExists(resource_path("lang/{$localeConfig}/{$this->lang_name}.php")); + + $this->assertFileExists(base_path("routes/web.php")); + $this->assertFileExists(app_path("Policies/{$this->model_name}Policy.php")); + $this->assertFileExists(database_path("factories/{$this->model_name}Factory.php")); + $this->assertFileExists(base_path("tests/Unit/Models/{$this->model_name}Test.php")); + $this->assertFileExists(base_path("tests/Feature/Manage{$this->plural_model_name}Test.php")); + } + + /** @test */ + public function it_cannot_generate_crud_files_if_model_exists() + { + $this->artisan('make:model', ['name' => $this->model_name, '--no-interaction' => true]); + $this->artisan('make:crud', ['name' => $this->model_name, '--no-interaction' => true]); + + $this->assertContains("{$this->model_name} model already exists.", app(Kernel::class)->output()); + + $this->assertFileExists(app_path($this->model_name.'.php')); + $this->assertFileNotExists(app_path("Http/Controllers/Api/{$this->plural_model_name}Controller.php")); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_'.$this->table_name.'_table.php'); + $this->assertFileNotExists($migrationFilePath); + + $this->assertFileNotExists(resource_path("views/{$this->table_name}/index.blade.php")); + $this->assertFileNotExists(resource_path("views/{$this->table_name}/forms.blade.php")); + + $localeConfig = config('app.locale'); + $this->assertFileNotExists(resource_path("lang/{$localeConfig}/{$this->lang_name}.php")); + + $this->assertFileNotExists(base_path("routes/web.php")); + $this->assertFileNotExists(app_path("Policies/{$this->model_name}Policy.php")); + $this->assertFileNotExists(database_path("factories/{$this->model_name}Factory.php")); + $this->assertFileNotExists(base_path("tests/Unit/Models/{$this->model_name}Test.php")); + $this->assertFileNotExists(base_path("tests/Feature/Manage{$this->plural_model_name}Test.php")); + } + + /** @test */ + public function it_cannot_generate_crud_files_if_namespaced_model_exists() + { + $this->artisan('make:model', ['name' => 'Entities/Projects/Problem', '--no-interaction' => true]); + $this->artisan('make:crud-api', ['name' => 'Entities/Projects/Problem', '--no-interaction' => true]); + + $this->assertContains("Problem model already exists.", app(Kernel::class)->output()); + + $this->assertFileExists(app_path('Entities/Projects/Problem.php')); + $this->assertFileNotExists(app_path("Http/Controllers/Api/ProblemsController.php")); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_problems_table.php'); + $this->assertFileNotExists($migrationFilePath); + + $this->assertFileNotExists(resource_path("views/problems/index.blade.php")); + $this->assertFileNotExists(resource_path("views/problems/forms.blade.php")); + + $localeConfig = config('app.locale'); + $this->assertFileNotExists(resource_path("lang/{$localeConfig}/{$this->lang_name}.php")); + + $this->assertFileNotExists(app_path("Policies/ProblemPolicy.php")); + $this->assertFileNotExists(database_path("factories/ProblemFactory.php")); + $this->assertFileNotExists(base_path("tests/Unit/Models/ProblemTest.php")); + $this->assertFileNotExists(base_path("tests/Feature/ManageProblemsTest.php")); + + $this->removeFileOrDir(app_path('Entities/Projects')); + $this->removeFileOrDir(resource_path('views/problems')); + $this->removeFileOrDir(resource_path("lang/en/problem.php")); + } + + /** @test */ + public function it_can_generate_crud_files_for_namespaced_model() + { + $inputName = 'Entities/References/Category'; + $modelName = 'Category'; + $pluralModelName = 'Categories'; + $tableName = 'categories'; + $langName = 'category'; + $modelPath = 'Entities/References'; + + $this->artisan('make:crud-api', ['name' => $inputName, '--no-interaction' => true]); + + $this->assertNotContains("{$modelName} model already exists.", app(Kernel::class)->output()); + + $this->assertFileExists(app_path($modelPath.'/'.$modelName.'.php')); + $this->assertFileExists(app_path("Http/Controllers/Api/{$pluralModelName}Controller.php")); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_'.$tableName.'_table.php'); + $this->assertFileExists($migrationFilePath); + + $this->assertFileExists(resource_path("views/{$tableName}/index.blade.php")); + $this->assertFileExists(resource_path("views/{$tableName}/forms.blade.php")); + + $localeConfig = config('app.locale'); + $this->assertFileExists(resource_path("lang/{$localeConfig}/{$langName}.php")); + + $this->assertFileExists(app_path("Policies/{$modelName}Policy.php")); + $this->assertFileExists(database_path("factories/{$modelName}Factory.php")); + $this->assertFileExists(base_path("tests/Unit/Models/{$modelName}Test.php")); + $this->assertFileExists(base_path("tests/Feature/Manage{$pluralModelName}Test.php")); + } + + /** @test */ + public function it_can_generate_crud_files_with_parent_option() + { + $inputName = 'Entities/References/Category'; + $modelName = 'Category'; + $parentName = 'Projects'; + $pluralModelName = 'Categories'; + $tableName = 'categories'; + $langName = 'category'; + $modelPath = 'Entities/References'; + + $this->artisan('make:crud-api', ['name' => $inputName, '--parent' => $parentName, '--no-interaction' => true]); + + $this->assertNotContains("{$modelName} model already exists.", app(Kernel::class)->output()); + + $this->assertFileExists(app_path($modelPath.'/'.$modelName.'.php')); + $this->assertFileExists(app_path("Http/Controllers/Api/{$parentName}/{$pluralModelName}Controller.php")); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_'.$tableName.'_table.php'); + $this->assertFileExists($migrationFilePath); + + $this->assertFileExists(resource_path("views/{$tableName}/index.blade.php")); + $this->assertFileExists(resource_path("views/{$tableName}/forms.blade.php")); + + $localeConfig = config('app.locale'); + $this->assertFileExists(resource_path("lang/{$localeConfig}/{$langName}.php")); + + $this->assertFileExists(database_path("factories/{$modelName}Factory.php")); + $this->assertFileExists(base_path("tests/Unit/Models/{$modelName}Test.php")); + $this->assertFileExists(app_path("Policies/{$parentName}/{$modelName}Policy.php")); + $this->assertFileExists(base_path("tests/Feature/Manage{$pluralModelName}Test.php")); + } +} diff --git a/tests/Generators/Api/ApiControllerGeneratorTest.php b/tests/Generators/Api/ApiControllerGeneratorTest.php new file mode 100644 index 0000000..a2d6142 --- /dev/null +++ b/tests/Generators/Api/ApiControllerGeneratorTest.php @@ -0,0 +1,106 @@ +artisan('make:crud-api', ['name' => $this->model_name, '--no-interaction' => true]); + + $this->assertFileExists(app_path("Http/Controllers/Api/{$this->plural_model_name}Controller.php")); + $ctrlClassContent = "full_model_name}; +use Illuminate\Http\Request; + +class {$this->plural_model_name}Controller extends Controller +{ + /** + * Display a listing of the {$this->single_model_var_name}. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + \${$this->collection_model_var_name} = {$this->model_name}::where(function (\$query) { + \$query->where('name', 'like', '%'.request('q').'%'); + })->paginate(25); + + return \${$this->collection_model_var_name}; + } + + /** + * Store a newly created {$this->single_model_var_name} in storage. + * + * @param \Illuminate\Http\Request \$request + * @return \Illuminate\Http\Response + */ + public function store(Request \$request) + { + \$this->authorize('create', new {$this->model_name}); + + \$this->validate(\$request, [ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + + \$new{$this->model_name} = \$request->only('name', 'description'); + \$new{$this->model_name}['creator_id'] = auth()->id(); + + \${$this->single_model_var_name} = {$this->model_name}::create(\$new{$this->model_name}); + + return \${$this->single_model_var_name}; + } + + /** + * Update the specified {$this->single_model_var_name} in storage. + * + * @param \Illuminate\Http\Request \$request + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\Http\Response + */ + public function update(Request \$request, {$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('update', \${$this->single_model_var_name}); + + \$this->validate(\$request, [ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + + \${$this->single_model_var_name}->update(\$request->only('name', 'description')); + + return \${$this->single_model_var_name}; + } + + /** + * Remove the specified {$this->single_model_var_name} from storage. + * + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\Http\Response + */ + public function destroy({$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('delete', \${$this->single_model_var_name}); + + \$this->validate(request(), [ + '{$this->lang_name}_id' => 'required', + ]); + + if (request('{$this->lang_name}_id') == \${$this->single_model_var_name}->id && \${$this->single_model_var_name}->delete()) { + return response()->json('{$this->lang_name} deleted.', 204); + } + + return response()->json('Unprocessable Entity.', 422); + } +} +"; + $this->assertEquals($ctrlClassContent, file_get_contents(app_path("Http/Controllers/Api/{$this->plural_model_name}Controller.php"))); + } +}