From 7b82a8571c4978f21e58c99c23592a31723b9c6b Mon Sep 17 00:00:00 2001 From: artosepyan Date: Thu, 28 Aug 2025 21:34:45 +0300 Subject: [PATCH 1/5] feat: add default HttpException render Refs: https://github.com/RonasIT/laravel-project-initializator/issues/40 --- src/Commands/InitCommand.php | 46 ++++++++++++++ tests/InitCommandTest.php | 60 ++++++++++++++++++- tests/fixtures/InitCommandTest/app.php | 18 ++++++ .../InitCommandTest/app_after_changes.php | 26 ++++++++ 4 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/InitCommandTest/app.php create mode 100644 tests/fixtures/InitCommandTest/app_after_changes.php diff --git a/src/Commands/InitCommand.php b/src/Commands/InitCommand.php index 8d1c08d..7a0f4f0 100644 --- a/src/Commands/InitCommand.php +++ b/src/Commands/InitCommand.php @@ -192,6 +192,8 @@ public function handle(): void $this->setAutoDocContactEmail($this->codeOwnerEmail); + $this->addDefaultHttpExceptionRender(); + Artisan::call('migrate'); } @@ -204,6 +206,50 @@ protected function setAutoDocContactEmail(string $email): void $config->write(); } + protected function addDefaultHttpExceptionRender(): void + { + $file = base_path('bootstrap/app.php'); + + $content = file_get_contents($file); + + $find = [ + '$exceptions->render(function (HttpException $exception, Request $request)', + '$exceptions->render(function (\Symfony\Component\HttpKernel\Exception\HttpException $exception, \Illuminate\Http\Request $request)', + ]; + + if (Str::contains($content, $find)) { + return; + } + + $imports = [ + 'use Symfony\Component\HttpKernel\Exception\HttpException;', + 'use Illuminate\Http\Request;', + ]; + + foreach ($imports as $import) { + if (!Str::contains($content, $import)) { + $content = preg_replace('/<\?php\s*/', "render(function (HttpException $exception, Request $request) { + return ($request->expectsJson()) + ? response()->json(['error' => $exception->getMessage()], $exception->getStatusCode()) + : null; + }); + PHP; + + $content = preg_replace( + '/(->withExceptions\(function \(Exceptions \$exceptions\)(?:\: void)? \{)/', + "$1\n" . $codeToAdd . "\n", + $content, + 1 + ); + + file_put_contents($file, $content); + } + protected function createAdminUser(string $kebabName): void { $defaultPassword = substr(md5(uniqid()), 0, 8); diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index 1d1fd4d..6a2d1d7 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -21,11 +21,19 @@ public function testRunWithoutAdminAndReadmeCreationConvertAppNameToPascalCaseTe 'arguments' => ['.env.development'], 'result' => $this->getFixture('env.development_app_name_pascal_case.yml'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( 'env.example_app_name_pascal_case.yml', 'env.development_app_name_pascal_case.yml', + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockClassExists([ @@ -67,6 +75,10 @@ public function testRunWithoutAdminAndReadmeCreation() 'arguments' => ['.env.development'], 'result' => $this->getFixture('env.development_app_name_pascal_case.yml'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -76,6 +88,10 @@ public function testRunWithoutAdminAndReadmeCreation() 'renovate.json', $this->getFixture('renovate.json'), ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( @@ -115,6 +131,10 @@ public function testRunWithAdminAndWithoutReadmeCreation() 'arguments' => ['.env.development'], 'result' => $this->getFixture('env.development.yml'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -124,6 +144,10 @@ public function testRunWithAdminAndWithoutReadmeCreation() 'database/migrations/2018_11_11_111111_add_default_user.php', $this->getFixture('migration.php'), ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( @@ -215,6 +239,10 @@ public function testRunWithAdminAndDefaultReadmeCreation() 'arguments' => [base_path('/vendor/ronasit/laravel-project-initializator/resources/md/readme/RENOVATE.md')], 'result' => $this->getTemplate('RENOVATE.md'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -248,6 +276,10 @@ public function testRunWithAdminAndDefaultReadmeCreation() 'README.md', $this->getFixture('default_readme_after_using_renovate.md'), ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( @@ -369,6 +401,10 @@ public function testRunWithAdminAndPartialReadmeCreation() 'arguments' => [base_path('/vendor/ronasit/laravel-project-initializator/resources/md/readme/CREDENTIALS_AND_ACCESS.md')], 'result' => $this->getTemplate('CREDENTIALS_AND_ACCESS.md'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -377,7 +413,11 @@ public function testRunWithAdminAndPartialReadmeCreation() [ 'README.md', $this->getFixture('partial_readme.md'), - ] + ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( @@ -498,6 +538,10 @@ public function testRunWithAdminAndFullReadmeCreationAndRemovingInitializatorIns 'arguments' => [base_path('/vendor/ronasit/laravel-project-initializator/resources/md/readme/RENOVATE.md')], 'result' => $this->getTemplate('RENOVATE.md'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -519,6 +563,10 @@ public function testRunWithAdminAndFullReadmeCreationAndRemovingInitializatorIns 'README.md', $this->getFixture('full_readme_after_using_renovate.md'), ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( @@ -637,6 +685,10 @@ public function testRunWithoutAdminAndUsingTelescope() 'arguments' => [base_path('/vendor/ronasit/laravel-project-initializator/resources/md/readme/CREDENTIALS_AND_ACCESS.md')], 'result' => $this->getTemplate('CREDENTIALS_AND_ACCESS.md'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -645,7 +697,11 @@ public function testRunWithoutAdminAndUsingTelescope() [ 'README.md', $this->getFixture('partial_readme_with_telescope.md'), - ] + ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( diff --git a/tests/fixtures/InitCommandTest/app.php b/tests/fixtures/InitCommandTest/app.php new file mode 100644 index 0000000..c183276 --- /dev/null +++ b/tests/fixtures/InitCommandTest/app.php @@ -0,0 +1,18 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + // + }) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/tests/fixtures/InitCommandTest/app_after_changes.php b/tests/fixtures/InitCommandTest/app_after_changes.php new file mode 100644 index 0000000..de444a2 --- /dev/null +++ b/tests/fixtures/InitCommandTest/app_after_changes.php @@ -0,0 +1,26 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + // + }) + ->withExceptions(function (Exceptions $exceptions): void { + $exceptions->render(function (HttpException $exception, Request $request) { + return ($request->expectsJson()) + ? response()->json(['error' => $exception->getMessage()], $exception->getStatusCode()) + : null; + }); + + // + })->create(); From 2e8b001963146eade3a8e004042893c0c34e04f8 Mon Sep 17 00:00:00 2001 From: artosepyan Date: Thu, 11 Sep 2025 15:42:35 +0300 Subject: [PATCH 2/5] fix: remarks from rewiever --- src/Commands/InitCommand.php | 26 +++++++++---------- src/ProjectInitializatorServiceProvider.php | 1 + stubs/default_http_exception.blade.php | 5 ++++ .../InitCommandTest/app_after_changes.php | 1 - 4 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 stubs/default_http_exception.blade.php diff --git a/src/Commands/InitCommand.php b/src/Commands/InitCommand.php index 7a0f4f0..e257c34 100644 --- a/src/Commands/InitCommand.php +++ b/src/Commands/InitCommand.php @@ -232,19 +232,19 @@ protected function addDefaultHttpExceptionRender(): void } } - $codeToAdd = <<<'PHP' - $exceptions->render(function (HttpException $exception, Request $request) { - return ($request->expectsJson()) - ? response()->json(['error' => $exception->getMessage()], $exception->getStatusCode()) - : null; - }); - PHP; - - $content = preg_replace( - '/(->withExceptions\(function \(Exceptions \$exceptions\)(?:\: void)? \{)/', - "$1\n" . $codeToAdd . "\n", - $content, - 1 + $codeToAdd = view('project-initialisator::default_http_exception')->render(); + + $baseIndent = ' '; + + $content = preg_replace_callback( + pattern: '/^([ \t]*)(->withExceptions\(function \(Exceptions \$exceptions\)(?:\: void)? \{)/m', + callback: function ($matches) use ($codeToAdd, $baseIndent) { + $currentIndent = $matches[1]; + $indentedCode = preg_replace('/^/m', $currentIndent . $baseIndent, $codeToAdd); + return $matches[0] . "\n" . $indentedCode; + }, + subject: $content, + limit: 1 ); file_put_contents($file, $content); diff --git a/src/ProjectInitializatorServiceProvider.php b/src/ProjectInitializatorServiceProvider.php index 4ea49e9..7958cd6 100644 --- a/src/ProjectInitializatorServiceProvider.php +++ b/src/ProjectInitializatorServiceProvider.php @@ -14,5 +14,6 @@ public function boot(): void ]); $this->loadViewsFrom(__DIR__ . '/../resources/views', 'initializator'); + $this->loadViewsFrom(__DIR__ . '/../stubs', 'project-initialisator'); } } diff --git a/stubs/default_http_exception.blade.php b/stubs/default_http_exception.blade.php new file mode 100644 index 0000000..2ae584b --- /dev/null +++ b/stubs/default_http_exception.blade.php @@ -0,0 +1,5 @@ +$exceptions->render(function (HttpException $exception, Request $request) { + return ($request->expectsJson()) + ? response()->json(['error' => $exception->getMessage()], $exception->getStatusCode()) + : null; +}); \ No newline at end of file diff --git a/tests/fixtures/InitCommandTest/app_after_changes.php b/tests/fixtures/InitCommandTest/app_after_changes.php index de444a2..b96ca44 100644 --- a/tests/fixtures/InitCommandTest/app_after_changes.php +++ b/tests/fixtures/InitCommandTest/app_after_changes.php @@ -21,6 +21,5 @@ ? response()->json(['error' => $exception->getMessage()], $exception->getStatusCode()) : null; }); - // })->create(); From a3c1a860f2970c20a1ec8bd684d52b63ceb40cf1 Mon Sep 17 00:00:00 2001 From: artosepyan Date: Thu, 11 Sep 2025 15:48:22 +0300 Subject: [PATCH 3/5] fix: remarks from rewiever --- src/Commands/InitCommand.php | 2 +- src/ProjectInitializatorServiceProvider.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/InitCommand.php b/src/Commands/InitCommand.php index e257c34..9f89989 100644 --- a/src/Commands/InitCommand.php +++ b/src/Commands/InitCommand.php @@ -232,7 +232,7 @@ protected function addDefaultHttpExceptionRender(): void } } - $codeToAdd = view('project-initialisator::default_http_exception')->render(); + $codeToAdd = view('project-initializator::default_http_exception')->render(); $baseIndent = ' '; diff --git a/src/ProjectInitializatorServiceProvider.php b/src/ProjectInitializatorServiceProvider.php index 7958cd6..1adcad2 100644 --- a/src/ProjectInitializatorServiceProvider.php +++ b/src/ProjectInitializatorServiceProvider.php @@ -14,6 +14,6 @@ public function boot(): void ]); $this->loadViewsFrom(__DIR__ . '/../resources/views', 'initializator'); - $this->loadViewsFrom(__DIR__ . '/../stubs', 'project-initialisator'); + $this->loadViewsFrom(__DIR__ . '/../stubs', 'project-initializator'); } } From 81d7fe9ae9299af06ba20ab6583abd4cb4a6124b Mon Sep 17 00:00:00 2001 From: artosepyan Date: Thu, 11 Sep 2025 15:58:54 +0300 Subject: [PATCH 4/5] test: fix tests after merging main --- tests/InitCommandTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index 3733f2c..6de2331 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -845,6 +845,10 @@ public function testRunWithClerkMobileApp(): void 'arguments' => [base_path('/vendor/ronasit/laravel-project-initializator/resources/md/readme/RENOVATE.md')], 'result' => $this->getTemplate('RENOVATE.md'), ], + [ + 'arguments' => [base_path('bootstrap/app.php')], + 'result' => $this->getFixture('app.php'), + ], ); $this->mockFilePutContent( @@ -882,6 +886,10 @@ public function testRunWithClerkMobileApp(): void 'README.md', $this->getFixture('default_readme_with_mobile_app_after_using_renovate.md'), ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockShellExec( From ed6bf5823c0791953c3891d08d7d4d3b6b4e205d Mon Sep 17 00:00:00 2001 From: artosepyan Date: Mon, 15 Sep 2025 14:59:23 +0300 Subject: [PATCH 5/5] test: fix tests after merge with main --- tests/InitCommandTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index 809c37a..cb0be9d 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -31,15 +31,15 @@ public function testRunWithoutAdminAndReadmeCreationConvertAppNameToPascalCaseTe $this->mockFilePutContent( 'env.example_app_name_pascal_case.yml', 'env.development_app_name_pascal_case.yml', - [ - base_path('bootstrap/app.php'), - $this->getFixture('app_after_changes.php'), - ], [ base_path('/routes/web.php'), "\nAuth::routes();\n", FILE_APPEND, ], + [ + base_path('bootstrap/app.php'), + $this->getFixture('app_after_changes.php'), + ], ); $this->mockClassExists([