@@ -16,27 +16,25 @@ try {
16
16
## Error Providers
17
17
18
18
Exceptions can implement the ` Tobyz\JsonApiServer\Exception\ErrorProvider `
19
- interface to determine what status code will be used in the response, and any
20
- JSON: API error objects to be rendered in the document.
19
+ interface to determine what status code will be used in the response, and define
20
+ a JSON: API error object to be rendered in the document.
21
21
22
22
The interface defines two methods:
23
23
24
- - ` getJsonApiStatus ` which must return a string.
25
- - ` getJsonApiErrors ` which must return an array of JSON: API error objects.
24
+ - ` getJsonApiStatus ` which must return the HTTP status code applicable to the
25
+ exception as a string.
26
+ - ` getJsonApiError ` which must return a JSON: API error object.
26
27
27
28
``` php
28
- use JsonApiPhp\JsonApi\Error;
29
29
use Tobyz\JsonApiServer\Exception\ErrorProvider;
30
30
31
31
class ImATeapotException implements ErrorProvider
32
32
{
33
- public function getJsonApiErrors (): array
33
+ public function getJsonApiError (): array
34
34
{
35
35
return [
36
- [
37
- 'title' => "I'm a teapot",
38
- 'status' => $this->getJsonApiStatus(),
39
- ],
36
+ 'title' => "I'm A Teapot",
37
+ 'status' => $this->getJsonApiStatus(),
40
38
];
41
39
}
42
40
@@ -50,8 +48,138 @@ class ImATeapotException implements ErrorProvider
50
48
Exceptions that do not implement this interface will result in a generic
51
49
` 500 Internal Server Error ` response.
52
50
51
+ ### Creating Custom Exceptions
52
+
53
+ The simplest way to create custom exceptions is to extend one of the base
54
+ exception classes like ` BadRequestException ` , ` UnprocessableEntityException ` , or
55
+ ` ForbiddenException ` . These base classes implement ` ErrorProvider ` and use the
56
+ ` JsonApiError ` trait internally to provide automatic error formatting.
57
+
58
+ For most cases, just extend a base exception and provide a message which will be
59
+ used at the error ` detail ` . For more control, you can set the ` $this->error `
60
+ array in your constructor, which will be merged with the defaults:
61
+
62
+ ``` php
63
+ use Tobyz\JsonApiServer\Exception\BadRequestException;
64
+
65
+ class ProductOutOfStockException extends BadRequestException
66
+ {
67
+ public function __construct(string $productId)
68
+ {
69
+ parent::__construct("Product $productId is out of stock");
70
+
71
+ $this->error = [
72
+ 'meta' => ['productId' => $productId],
73
+ 'links' => [
74
+ 'type' =>
75
+ 'https://example.com/docs/errors#product_out_of_stock',
76
+ ],
77
+ ];
78
+ }
79
+ }
80
+ ```
81
+
82
+ This automatically generates:
83
+
84
+ - ` code ` : ` product_out_of_stock ` (derived from class name)
85
+ - ` title ` : ` Product Out Of Stock ` (derived from class name)
86
+ - ` detail ` : ` Product ABC123 is out of stock ` (from constructor message)
87
+ - ` status ` : ` 400 ` (inherited from BadRequestException)
88
+
89
+ #### Helper Methods
90
+
91
+ The ` JsonApiError ` trait also provides fluent helper methods for modifying the
92
+ error object from the context in which it is thrown:
93
+
94
+ ``` php
95
+ throw (new UnknownFieldException('email'))
96
+ ->source(['pointer' => '/data/attributes/email'])
97
+ ->meta(['suggestion' => 'Did you mean "emailAddress"?'])
98
+ ->links(['about' => 'https://example.com/docs/fields']);
99
+ ```
100
+
101
+ ## Multiple Errors
102
+
103
+ When multiple validation errors occur (e.g., multiple field validation
104
+ failures), you can wrap them in ` JsonApiErrorsException ` .
105
+
106
+ ``` php
107
+ use Tobyz\JsonApiServer\Exception\JsonApiErrorsException;
108
+ use Tobyz\JsonApiServer\Exception\RequiredFieldException;
109
+ use Tobyz\JsonApiServer\Exception\InvalidFieldValueException;
110
+
111
+ throw new JsonApiErrorsException([
112
+ new RequiredFieldException(),
113
+ new InvalidFieldValueException('Must be a valid email address'),
114
+ ])->prependSource(['pointer' => '/data/attributes/email']);
115
+ ```
116
+
117
+ This will return a JSON: API error response with multiple error objects:
118
+
119
+ ``` json
120
+ {
121
+ "errors" : [
122
+ {
123
+ "status" : " 422" ,
124
+ "code" : " required_field" ,
125
+ "title" : " Required Field" ,
126
+ "detail" : " Field is required" ,
127
+ "source" : { "pointer" : " /data/attributes/email" }
128
+ },
129
+ {
130
+ "status" : " 422" ,
131
+ "code" : " invalid_field_value" ,
132
+ "title" : " Invalid Field Value" ,
133
+ "detail" : " Must be a valid email address" ,
134
+ "source" : { "pointer" : " /data/attributes/email" }
135
+ }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ When ` JsonApiErrorsException ` contains multiple errors with different status
141
+ codes, it automatically determines the most generally applicable HTTP error code
142
+ to be used in the response.
143
+
53
144
## Customizing Error Messages
54
145
55
- Many of the built-in error messages can be customized using the
56
- [ localization system] ( localization.md ) . This allows you to provide localized or
57
- custom error messages throughout your API.
146
+ All built-in exceptions include sensible default English error messages, so they
147
+ work out-of-the-box without any configuration. The ` code ` and ` title ` are
148
+ automatically derived from the exception class name, and each exception provides
149
+ a default ` detail ` message.
150
+
151
+ You can customize error messages for each exception using the ` errors() ` method
152
+ on your ` JsonApi ` instance. You can override any part of the error object for
153
+ any exception by providing exception class names as keys.
154
+
155
+ ``` php
156
+ use Tobyz\JsonApiServer\Exception\MethodNotAllowedException;
157
+ use Tobyz\JsonApiServer\Exception\ResourceNotFoundException;
158
+
159
+ $api->errors([
160
+ MethodNotAllowedException::class => [
161
+ 'title' => 'Not Allowed',
162
+ 'detail' => 'The :method method is not allowed for this endpoint',
163
+ ],
164
+ ResourceNotFoundException::class => [
165
+ 'title' => 'Not Found',
166
+ 'detail' => 'Could not find :type resource with ID :id',
167
+ ],
168
+ ]);
169
+ ```
170
+
171
+ ### Placeholder Replacement
172
+
173
+ The ` detail ` property supports placeholder replacement using the ` :placeholder `
174
+ syntax. Placeholders are replaced with values found in the error object's ` meta `
175
+ data:
176
+
177
+ ``` php
178
+ ResourceNotFoundException::class => [
179
+ 'detail' => 'Could not find :type resource with ID :id',
180
+ ]
181
+ ```
182
+
183
+ When a ` ResourceNotFoundException ` is thrown with ` meta ` containing
184
+ ` ['type' => 'users', 'id' => '123'] ` , the detail becomes: "Could not find users
185
+ resource with ID 123"
0 commit comments