From ea141f2c28cd3c429c845d5034fc2f7b4c180120 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Tue, 17 Feb 2026 18:35:36 +0100 Subject: [PATCH 1/4] feat: use values from ProblemExceptionInterface --- src/Laravel/ApiResource/Error.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Laravel/ApiResource/Error.php b/src/Laravel/ApiResource/Error.php index 771dbdb8d6..a85c3c3579 100644 --- a/src/Laravel/ApiResource/Error.php +++ b/src/Laravel/ApiResource/Error.php @@ -136,6 +136,10 @@ public static function createFromException(\Exception|\Throwable $exception, int { $headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : []; + if ($exception instanceof ProblemExceptionInterface) { + return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers); + } + return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: '/errors/'.$status, headers: $headers); } From 998b38071656c2b333c5c1c29406ecef53d817a0 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Tue, 17 Feb 2026 22:48:49 +0100 Subject: [PATCH 2/4] Add test case to JsonProblemTest --- src/Laravel/Tests/JsonProblemTest.php | 14 +++++ .../app/ApiResource/ServiceProvider.php | 2 + .../app/Exceptions/TeapotException.php | 55 +++++++++++++++++++ .../workbench/app/State/TeapotProvider.php | 30 ++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/Laravel/workbench/app/Exceptions/TeapotException.php create mode 100644 src/Laravel/workbench/app/State/TeapotProvider.php diff --git a/src/Laravel/Tests/JsonProblemTest.php b/src/Laravel/Tests/JsonProblemTest.php index f2dc6cb09d..4302750671 100644 --- a/src/Laravel/Tests/JsonProblemTest.php +++ b/src/Laravel/Tests/JsonProblemTest.php @@ -85,6 +85,20 @@ public function testRetrieveErrorHtml(): void ', $response->getContent()); } + public function testProblemExceptionInterface(): void + { + $response = $this->get('/api/teapot', headers: ['accept' => 'application/json']); + $response->assertStatus(418); + $response->assertHeader('content-type', 'application/problem+json; charset=utf-8'); + $response->assertJsonFragment([ + 'type' => '/problem/teapot', + 'title' => 'I\'m a teapot', + 'status' => 418, + 'detail' => 'No coffee here', + 'instance' => '/teapot', + ]); + } + /** * @return list}> */ diff --git a/src/Laravel/workbench/app/ApiResource/ServiceProvider.php b/src/Laravel/workbench/app/ApiResource/ServiceProvider.php index 81c2f032fb..2ba78a05c5 100644 --- a/src/Laravel/workbench/app/ApiResource/ServiceProvider.php +++ b/src/Laravel/workbench/app/ApiResource/ServiceProvider.php @@ -16,7 +16,9 @@ use ApiPlatform\Metadata\Get; use Workbench\App\State\CustomProvider; use Workbench\App\State\CustomProviderWithDependency; +use Workbench\App\State\TeapotProvider; +#[Get(uriTemplate: 'teapot', name: 'teapot', provider: TeapotProvider::class)] #[Get(uriTemplate: 'custom_service_provider', provider: CustomProvider::class)] #[Get(uriTemplate: 'custom_service_provider_with_dependency', provider: CustomProviderWithDependency::class)] class ServiceProvider diff --git a/src/Laravel/workbench/app/Exceptions/TeapotException.php b/src/Laravel/workbench/app/Exceptions/TeapotException.php new file mode 100644 index 0000000000..ef9b2b088c --- /dev/null +++ b/src/Laravel/workbench/app/Exceptions/TeapotException.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Workbench\App\Exceptions; + +use ApiPlatform\Metadata\Exception\ProblemExceptionInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + +class TeapotException extends \Exception implements ProblemExceptionInterface, HttpExceptionInterface +{ + public function getStatusCode(): int + { + return 418; + } + + public function getHeaders(): array + { + return []; + } + + public function getType(): string + { + return '/problem/teapot'; + } + + public function getTitle(): ?string + { + return 'I\'m a teapot'; + } + + public function getStatus(): ?int + { + return 418; + } + + public function getDetail(): ?string + { + return 'No coffee here'; + } + + public function getInstance(): ?string + { + return '/teapot'; + } +} diff --git a/src/Laravel/workbench/app/State/TeapotProvider.php b/src/Laravel/workbench/app/State/TeapotProvider.php new file mode 100644 index 0000000000..1570d46adf --- /dev/null +++ b/src/Laravel/workbench/app/State/TeapotProvider.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Workbench\App\State; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; +use Workbench\App\ApiResource\ServiceProvider; +use Workbench\App\Exceptions\TeapotException; + +/** + * @implements ProviderInterface + */ +class TeapotProvider implements ProviderInterface +{ + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + throw new TeapotException('I\'m boiling'); + } +} From 2a6bd07ac087c5bc2d062053303dc02bb69c0676 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Wed, 18 Feb 2026 11:53:00 +0100 Subject: [PATCH 3/4] Apply change to State/ApiResource/Error.php as well --- src/State/ApiResource/Error.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/State/ApiResource/Error.php b/src/State/ApiResource/Error.php index 09c034c3d8..ebee62fc08 100644 --- a/src/State/ApiResource/Error.php +++ b/src/State/ApiResource/Error.php @@ -199,6 +199,10 @@ public static function createFromException(\Exception|\Throwable $exception, int { $headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : []; + if ($exception instanceof ProblemExceptionInterface) { + return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers); + } + return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: "/errors/$status", headers: $headers, previous: $exception->getPrevious()); } From df95d60605eaa6eb08993de5bedc7744f860e821 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Wed, 18 Feb 2026 12:01:09 +0100 Subject: [PATCH 4/4] Fix StrictParametersTest in line with detail of ParameterNotSupportedException --- tests/Functional/Parameters/StrictParametersTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Functional/Parameters/StrictParametersTest.php b/tests/Functional/Parameters/StrictParametersTest.php index 3f918d8ed4..3a973b8338 100644 --- a/tests/Functional/Parameters/StrictParametersTest.php +++ b/tests/Functional/Parameters/StrictParametersTest.php @@ -34,7 +34,7 @@ public static function getResources(): array public function testErrorAsParameterIsNotAllowed(): void { self::createClient()->request('GET', 'strict_query_parameters?bar=test'); - $this->assertJsonContains(['detail' => 'Parameter not supported']); + $this->assertJsonContains(['detail' => 'Parameter "bar" not supported']); $this->assertResponseStatusCodeSame(400); }