From 1e01417cbe2a970baca22aedab420d94da2e5c3a Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Thu, 15 Apr 2021 23:32:35 +0800 Subject: [PATCH 1/3] Update model, migration, and factory for UUID --- src/CrudApiMake.php | 3 +- src/CrudMake.php | 3 +- src/CrudSimpleMake.php | 3 +- src/Generators/MigrationGenerator.php | 8 +- src/Generators/ModelFactoryGenerator.php | 6 + src/Generators/ModelGenerator.php | 8 ++ tests/CommandOptions/UuidOptionsTest.php | 134 +++++++++++++++++++++++ 7 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 tests/CommandOptions/UuidOptionsTest.php diff --git a/src/CrudApiMake.php b/src/CrudApiMake.php index 56f6b14..810fdf7 100644 --- a/src/CrudApiMake.php +++ b/src/CrudApiMake.php @@ -13,7 +13,8 @@ class CrudApiMake extends GeneratorCommand {--p|parent= : The generated API controller parent directory} {--t|tests-only : Generate API CRUD testcases only} {--r|form-requests : Generate CRUD with Form Request on create and update actions} - {--f|formfield : Generate CRUD with FormField facades}'; + {--f|formfield : Generate CRUD with FormField facades} + {--uuid : Generate CRUD with UUID primary keys}'; /** * The console command description. diff --git a/src/CrudMake.php b/src/CrudMake.php index 6afeafb..3ba43e5 100644 --- a/src/CrudMake.php +++ b/src/CrudMake.php @@ -14,7 +14,8 @@ class CrudMake extends GeneratorCommand {--t|tests-only : Generate CRUD testcases only} {--f|formfield : Generate CRUD with FormField facades} {--r|form-requests : Generate CRUD with Form Request on create and update actions} - {--bs3 : Generate CRUD with Bootstrap 3 views}'; + {--bs3 : Generate CRUD with Bootstrap 3 views} + {--uuid : Generate CRUD with UUID primary keys}'; /** * The console command description. diff --git a/src/CrudSimpleMake.php b/src/CrudSimpleMake.php index 3bdf7f1..7d38f96 100644 --- a/src/CrudSimpleMake.php +++ b/src/CrudSimpleMake.php @@ -14,7 +14,8 @@ class CrudSimpleMake extends GeneratorCommand {--t|tests-only : Generate CRUD testcases only} {--f|formfield : Generate CRUD with FormField facades} {--r|form-requests : Generate CRUD with Form Request on create and update actions} - {--bs3 : Generate CRUD with Bootstrap 3 views}'; + {--bs3 : Generate CRUD with Bootstrap 3 views} + {--uuid : Generate CRUD with UUID primary keys}'; /** * The console command description. diff --git a/src/Generators/MigrationGenerator.php b/src/Generators/MigrationGenerator.php index 7623ff5..f5772f8 100644 --- a/src/Generators/MigrationGenerator.php +++ b/src/Generators/MigrationGenerator.php @@ -28,6 +28,12 @@ public function generate(string $type = 'full') */ public function getContent(string $stubName) { - return $this->replaceStubString($this->getStubFileContent($stubName)); + $content = $this->replaceStubString($this->getStubFileContent($stubName)); + + if ($this->command->option('uuid')) { + $content = str_replace("\$table->bigIncrements('id')", "\$table->uuid('id')", $content); + } + + return $content; } } diff --git a/src/Generators/ModelFactoryGenerator.php b/src/Generators/ModelFactoryGenerator.php index c1e47da..046adf4 100644 --- a/src/Generators/ModelFactoryGenerator.php +++ b/src/Generators/ModelFactoryGenerator.php @@ -41,6 +41,12 @@ public function getContent(string $stubName) $modelFactoryFileContent = str_replace('App\User', $userModel, $modelFactoryFileContent); } + if ($this->command->option('uuid')) { + $string = "'title' => \$this->faker->word,\n"; + $replacement = "'id' => \$this->faker->uuid,\n 'title' => \$this->faker->word,\n"; + $modelFactoryFileContent = str_replace($string, $replacement, $modelFactoryFileContent); + } + return $this->replaceStubString($modelFactoryFileContent); } } diff --git a/src/Generators/ModelGenerator.php b/src/Generators/ModelGenerator.php index 79940a5..bf066ae 100644 --- a/src/Generators/ModelGenerator.php +++ b/src/Generators/ModelGenerator.php @@ -43,6 +43,14 @@ public function getContent(string $stubName) $modelFileContent = str_replace('App\User', $userModel, $modelFileContent); } + if ($this->command->option('uuid')) { + $string = "protected \$fillable = ['title', 'description', 'creator_id'];\n"; + $replacement = "public \$incrementing = false;\n\n"; + $replacement .= " protected \$keyType = 'string';\n\n"; + $replacement .= " protected \$fillable = ['id', 'title', 'description', 'creator_id'];\n"; + $modelFileContent = str_replace($string, $replacement, $modelFileContent); + } + return $this->replaceStubString($modelFileContent); } } diff --git a/tests/CommandOptions/UuidOptionsTest.php b/tests/CommandOptions/UuidOptionsTest.php new file mode 100644 index 0000000..5a1cf9d --- /dev/null +++ b/tests/CommandOptions/UuidOptionsTest.php @@ -0,0 +1,134 @@ +artisan('make:crud', ['name' => $this->model_name, '--uuid' => true, '--no-interaction' => true]); + + $migrationFilePath = database_path('migrations/'.date('Y_m_d_His').'_create_'.$this->table_name.'_table.php'); + $this->assertFileExists($migrationFilePath); + $modelClassContent = "plural_model_name}Table extends Migration +{ + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::create('{$this->table_name}', function (Blueprint \$table) { + \$table->uuid('id'); + \$table->string('title', 60); + \$table->string('description')->nullable(); + \$table->foreignId('creator_id')->constrained('users')->onDelete('restrict'); + \$table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('{$this->table_name}'); + } +} +"; + $this->assertEquals($modelClassContent, file_get_contents($migrationFilePath)); + } + /** @test */ + public function it_creates_correct_model_factory_content_for_uuid_primary_key() + { + $this->artisan('make:crud', ['name' => $this->model_name, '--uuid' => true, '--no-interaction' => true]); + + $modelFactoryPath = database_path('factories/'.$this->model_name.'Factory.php'); + $this->assertFileExists($modelFactoryPath); + $modelFactoryContent = "full_model_name}; +use Illuminate\Database\Eloquent\Factories\Factory; + +class {$this->model_name}Factory extends Factory +{ + protected \$model = {$this->model_name}::class; + + public function definition() + { + return [ + 'id' => \$this->faker->uuid, + 'title' => \$this->faker->word, + 'description' => \$this->faker->sentence, + 'creator_id' => function () { + return User::factory()->create()->id; + }, + ]; + } +} +"; + $this->assertEquals($modelFactoryContent, file_get_contents($modelFactoryPath)); + } + /** @test */ + public function it_creates_correct_model_class_content_for_uuid_primary_key() + { + config(['auth.providers.users.model' => 'App\Models\User']); + $this->artisan('make:crud', ['name' => $this->model_name, '--uuid' => true, '--no-interaction' => true]); + + $modelPath = app_path('Models/'.$this->model_name.'.php'); + $this->assertFileExists($modelPath); + $modelClassContent = "model_name} extends Model +{ + use HasFactory; + + public \$incrementing = false; + + protected \$keyType = 'string'; + + protected \$fillable = ['id', 'title', 'description', 'creator_id']; + + public function getTitleLinkAttribute() + { + \$title = __('app.show_detail_title', [ + 'title' => \$this->title, 'type' => __('{$this->lang_name}.{$this->lang_name}'), + ]); + \$link = 'table_name}.show', \$this).'\"'; + \$link .= ' title=\"'.\$title.'\">'; + \$link .= \$this->title; + \$link .= ''; + + return \$link; + } + + public function creator() + { + return \$this->belongsTo(User::class); + } +} +"; + $this->assertEquals($modelClassContent, file_get_contents($modelPath)); + } +} From 175b31edbb14dbf5f4d58d35ef96b962f5773391 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Thu, 15 Apr 2021 23:55:28 +0800 Subject: [PATCH 2/3] Update controller for UUID --- src/Generators/ControllerGenerator.php | 12 +++ tests/CommandOptions/UuidOptionsTest.php | 106 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/Generators/ControllerGenerator.php b/src/Generators/ControllerGenerator.php index a1427e5..ca6a2f2 100644 --- a/src/Generators/ControllerGenerator.php +++ b/src/Generators/ControllerGenerator.php @@ -73,6 +73,18 @@ public function getContent(string $stubName) ); } + if ($this->command->option('uuid')) { + $string = "use Illuminate\Http\Request;\n"; + $replacement = "use Illuminate\Http\Request;\nuse Ramsey\Uuid\Uuid;\n"; + $controllerFileContent = str_replace($string, $replacement, $controllerFileContent); + + $string = "\$newMemberType['creator_id'] = auth()->id();\n"; + $replacement = "\$new{$this->modelNames['model_name']}['id'] = Uuid::uuid4()->toString();\n"; + $replacement .= " \$new{$this->modelNames['model_name']}['creator_id'] = auth()->id();\n"; + + $controllerFileContent = str_replace($string, $replacement, $controllerFileContent); + } + return $controllerFileContent; } } diff --git a/tests/CommandOptions/UuidOptionsTest.php b/tests/CommandOptions/UuidOptionsTest.php index 5a1cf9d..453b0d3 100644 --- a/tests/CommandOptions/UuidOptionsTest.php +++ b/tests/CommandOptions/UuidOptionsTest.php @@ -131,4 +131,110 @@ public function creator() "; $this->assertEquals($modelClassContent, file_get_contents($modelPath)); } + /** @test */ + public function it_creates_correct_controller_class_content_for_uuid_primary_key() + { + $this->artisan('make:crud-simple', ['name' => $this->model_name, '--uuid' => true, '--no-interaction' => true]); + + $this->assertFileExists(app_path("Http/Controllers/{$this->model_name}Controller.php")); + $ctrlClassContent = "full_model_name}; +use Illuminate\Http\Request; +use Ramsey\Uuid\Uuid; + +class {$this->model_name}Controller extends Controller +{ + /** + * Display a listing of the {$this->single_model_var_name}. + * + * @param \Illuminate\Http\Request \$request + * @return \Illuminate\View\View + */ + public function index(Request \$request) + { + \$editable{$this->model_name} = null; + \${$this->single_model_var_name}Query = {$this->model_name}::query(); + \${$this->single_model_var_name}Query->where('title', 'like', '%'.\$request->get('q').'%'); + \${$this->single_model_var_name}Query->orderBy('title'); + \${$this->collection_model_var_name} = \${$this->single_model_var_name}Query->paginate(25); + + if (in_array(request('action'), ['edit', 'delete']) && request('id') != null) { + \$editable{$this->model_name} = {$this->model_name}::find(request('id')); + } + + return view('{$this->table_name}.index', compact('{$this->collection_model_var_name}', 'editable{$this->model_name}')); + } + + /** + * Store a newly created {$this->single_model_var_name} in storage. + * + * @param \Illuminate\Http\Request \$request + * @return \Illuminate\Routing\Redirector + */ + public function store(Request \$request) + { + \$this->authorize('create', new {$this->model_name}); + + \$new{$this->model_name} = \$request->validate([ + 'title' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + \$new{$this->model_name}['id'] = Uuid::uuid4()->toString(); + \$new{$this->model_name}['creator_id'] = auth()->id(); + + {$this->model_name}::create(\$new{$this->model_name}); + + return redirect()->route('{$this->table_name}.index'); + } + + /** + * 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\Routing\Redirector + */ + public function update(Request \$request, {$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('update', \${$this->single_model_var_name}); + + \${$this->single_model_var_name}Data = \$request->validate([ + 'title' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + \${$this->single_model_var_name}->update(\${$this->single_model_var_name}Data); + + \$routeParam = request()->only('page', 'q'); + + return redirect()->route('{$this->table_name}.index', \$routeParam); + } + + /** + * Remove the specified {$this->single_model_var_name} from storage. + * + * @param \Illuminate\Http\Request \$request + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\Routing\Redirector + */ + public function destroy(Request \$request, {$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('delete', \${$this->single_model_var_name}); + + \$request->validate(['{$this->lang_name}_id' => 'required']); + + if (\$request->get('{$this->lang_name}_id') == \${$this->single_model_var_name}->id && \${$this->single_model_var_name}->delete()) { + \$routeParam = request()->only('page', 'q'); + + return redirect()->route('{$this->table_name}.index', \$routeParam); + } + + return back(); + } +} +"; + $this->assertEquals($ctrlClassContent, file_get_contents(app_path("Http/Controllers/{$this->model_name}Controller.php"))); + } } From f7474a925335d9dfeb178b55b76f4eaceaa5baef Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Fri, 16 Apr 2021 09:09:49 +0800 Subject: [PATCH 3/3] Update invalid string search --- src/Generators/ControllerGenerator.php | 2 +- tests/CommandOptions/UuidOptionsTest.php | 134 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/Generators/ControllerGenerator.php b/src/Generators/ControllerGenerator.php index ca6a2f2..bfefe80 100644 --- a/src/Generators/ControllerGenerator.php +++ b/src/Generators/ControllerGenerator.php @@ -78,7 +78,7 @@ public function getContent(string $stubName) $replacement = "use Illuminate\Http\Request;\nuse Ramsey\Uuid\Uuid;\n"; $controllerFileContent = str_replace($string, $replacement, $controllerFileContent); - $string = "\$newMemberType['creator_id'] = auth()->id();\n"; + $string = "\$new{$this->modelNames['model_name']}['creator_id'] = auth()->id();\n"; $replacement = "\$new{$this->modelNames['model_name']}['id'] = Uuid::uuid4()->toString();\n"; $replacement .= " \$new{$this->modelNames['model_name']}['creator_id'] = auth()->id();\n"; diff --git a/tests/CommandOptions/UuidOptionsTest.php b/tests/CommandOptions/UuidOptionsTest.php index 453b0d3..e45ad82 100644 --- a/tests/CommandOptions/UuidOptionsTest.php +++ b/tests/CommandOptions/UuidOptionsTest.php @@ -234,6 +234,140 @@ public function destroy(Request \$request, {$this->model_name} \${$this->single_ return back(); } } +"; + $this->assertEquals($ctrlClassContent, file_get_contents(app_path("Http/Controllers/{$this->model_name}Controller.php"))); + } + + /** @test */ + public function it_creates_correct_full_controller_class_content_for_uuid_primary_key() + { + $this->artisan('make:crud', ['name' => $this->model_name, '--uuid' => true, '--no-interaction' => true]); + + $this->assertFileExists(app_path("Http/Controllers/{$this->model_name}Controller.php")); + $ctrlClassContent = "full_model_name}; +use Illuminate\Http\Request; +use Ramsey\Uuid\Uuid; + +class {$this->model_name}Controller extends Controller +{ + /** + * Display a listing of the {$this->single_model_var_name}. + * + * @param \Illuminate\Http\Request \$request + * @return \Illuminate\View\View + */ + public function index(Request \$request) + { + \${$this->single_model_var_name}Query = {$this->model_name}::query(); + \${$this->single_model_var_name}Query->where('title', 'like', '%'.\$request->get('q').'%'); + \${$this->single_model_var_name}Query->orderBy('title'); + \${$this->collection_model_var_name} = \${$this->single_model_var_name}Query->paginate(25); + + return view('{$this->table_name}.index', compact('{$this->collection_model_var_name}')); + } + + /** + * Show the form for creating a new {$this->single_model_var_name}. + * + * @return \Illuminate\View\View + */ + public function create() + { + \$this->authorize('create', new {$this->model_name}); + + return view('{$this->table_name}.create'); + } + + /** + * Store a newly created {$this->single_model_var_name} in storage. + * + * @param \Illuminate\Http\Request \$request + * @return \Illuminate\Routing\Redirector + */ + public function store(Request \$request) + { + \$this->authorize('create', new {$this->model_name}); + + \$new{$this->model_name} = \$request->validate([ + 'title' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + \$new{$this->model_name}['id'] = Uuid::uuid4()->toString(); + \$new{$this->model_name}['creator_id'] = auth()->id(); + + \${$this->single_model_var_name} = {$this->model_name}::create(\$new{$this->model_name}); + + return redirect()->route('{$this->table_name}.show', \${$this->single_model_var_name}); + } + + /** + * Display the specified {$this->single_model_var_name}. + * + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\View\View + */ + public function show({$this->model_name} \${$this->single_model_var_name}) + { + return view('{$this->table_name}.show', compact('{$this->single_model_var_name}')); + } + + /** + * Show the form for editing the specified {$this->single_model_var_name}. + * + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\View\View + */ + public function edit({$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('update', \${$this->single_model_var_name}); + + return view('{$this->table_name}.edit', compact('{$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\Routing\Redirector + */ + public function update(Request \$request, {$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('update', \${$this->single_model_var_name}); + + \${$this->single_model_var_name}Data = \$request->validate([ + 'title' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + \${$this->single_model_var_name}->update(\${$this->single_model_var_name}Data); + + return redirect()->route('{$this->table_name}.show', \${$this->single_model_var_name}); + } + + /** + * Remove the specified {$this->single_model_var_name} from storage. + * + * @param \Illuminate\Http\Request \$request + * @param \\{$this->full_model_name} \${$this->single_model_var_name} + * @return \Illuminate\Routing\Redirector + */ + public function destroy(Request \$request, {$this->model_name} \${$this->single_model_var_name}) + { + \$this->authorize('delete', \${$this->single_model_var_name}); + + \$request->validate(['{$this->lang_name}_id' => 'required']); + + if (\$request->get('{$this->lang_name}_id') == \${$this->single_model_var_name}->id && \${$this->single_model_var_name}->delete()) { + return redirect()->route('{$this->table_name}.index'); + } + + return back(); + } +} "; $this->assertEquals($ctrlClassContent, file_get_contents(app_path("Http/Controllers/{$this->model_name}Controller.php"))); }