diff --git a/app/bootstrap/app.php b/app/bootstrap/app.php index 1ad2dd6b..f4e0d7d2 100644 --- a/app/bootstrap/app.php +++ b/app/bootstrap/app.php @@ -2,9 +2,12 @@ use App\Http\Middleware\EnsureSaasAdmin; use App\Http\Middleware\SetTenantContext; +use Illuminate\Database\QueryException; use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( @@ -30,5 +33,18 @@ return Application::configure(basePath: dirname(__DIR__)) ]); }) ->withExceptions(function (Exceptions $exceptions): void { - // + $exceptions->render(function (QueryException $e, Request $request) { + Log::error('db.query_exception', [ + 'message' => $e->getMessage(), + 'sql' => $e->getSql(), + 'path' => $request->path(), + ]); + if ($request->expectsJson()) { + return response()->json([ + 'message' => 'Не удалось сохранить. Проверьте данные или попробуйте ещё раз.', + ], 422); + } + + return null; // default render for non-JSON + }); })->create(); diff --git a/app/phpstan-baseline.neon b/app/phpstan-baseline.neon index 4b541d69..e3356923 100644 --- a/app/phpstan-baseline.neon +++ b/app/phpstan-baseline.neon @@ -258,6 +258,90 @@ parameters: count: 1 path: app/Services/Supplier/SupplierProjectGrouping.php + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\ForbiddenDefineFunctions not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\ForbiddenFinalClasses not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\ForbiddenNormalClasses not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\ForbiddenPrivateMethods not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\ForbiddenTraits not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Insights\\SyntaxCheck not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class NunoMaduro\\PhpInsights\\Domain\\Metrics\\Architecture\\Classes not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\Commenting\\UselessFunctionDocCommentSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\Namespaces\\AlphabeticallySortedUsesSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\TypeHints\\DeclareStrictTypesSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\TypeHints\\DisallowMixedTypeHintSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\TypeHints\\ParameterTypeHintSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\TypeHints\\PropertyTypeHintSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + + - + message: '#^Class SlevomatCodingStandard\\Sniffs\\TypeHints\\ReturnTypeHintSniff not found\.$#' + identifier: class.notFound + count: 1 + path: config/insights.php + - message: '#^Return type \(array\\) of method Database\\Factories\\BalanceTransactionFactory\:\:definition\(\) should be compatible with return type \(array\\) of method Illuminate\\Database\\Eloquent\\Factories\\Factory\\:\:definition\(\)$#' identifier: method.childReturnType @@ -1572,6 +1656,12 @@ parameters: count: 14 path: tests/Feature/Plan5/Projects/ProjectsUpdateTest.php + - + message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:getJson\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Feature/Project/QueryExceptionRenderTest.php + - message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#' identifier: property.notFound diff --git a/app/tests/Feature/Project/QueryExceptionRenderTest.php b/app/tests/Feature/Project/QueryExceptionRenderTest.php new file mode 100644 index 00000000..b88c45f0 --- /dev/null +++ b/app/tests/Feature/Project/QueryExceptionRenderTest.php @@ -0,0 +1,17 @@ +getJson('/_test/boom-query'); + $res->assertStatus(422); + expect($res->json('message'))->not->toContain('SQLSTATE'); + expect($res->json('message'))->toContain('Не удалось'); +});