You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: source/devapi/controllers.rst
+76-23Lines changed: 76 additions & 23 deletions
Original file line number
Diff line number
Diff line change
@@ -5,21 +5,21 @@ You need a `Controller` any time you want an URL access.
5
5
6
6
.. note::
7
7
8
-
`Controllers` is the "modern" way that replaces all files previousely present in ``front/`` and ``ajax/`` directories.
8
+
`Controllers` are the modern way that replace all files previousely present in ``front/`` and ``ajax/`` directories.
9
9
10
10
.. warning::
11
11
12
-
Currently, not all existing front or ajax files has been migrated to `Controllers`, mainly because of specific stuff or no time to work on that yet.
12
+
Currently, not all existing ``front/`` or ``ajax/`` files have been migrated to `Controllers`, mainly because of specific behaviors or lack of time to work on migrating them.
13
13
14
-
Any new feature added to GLPI >=11 **must** use `Controllers`.
14
+
Any new feature added to GLPI >=11 **must** use `Controllers`.
15
15
16
16
Creating a controller
17
17
^^^^^^^^^^^^^^^^^^^^^
18
18
19
19
Minimal requirements to have a working controller:
20
20
21
-
* The controller file must be placed in the src/Glpi/Controller/** folder.
22
-
* The name of the controller must end with Controller.
21
+
* The controller file must be placed in the ``src/Glpi/Controller/`` folder.
22
+
* The name of the controller must end with ``Controller``.
23
23
* The controller must extends the ``Glpi\Controller\AbstractController`` class.
24
24
* The controller must define a route using the Route attribute.
25
25
* The controller must return some kind of response.
@@ -97,7 +97,7 @@ You will thus need to prefix your route by ``/ajax`` until we find a better way
97
97
Reading query parameters
98
98
^^^^^^^^^^^^^^^^^^^^^^^^
99
99
100
-
These parameters are found in the $request object:
100
+
These parameters are found in the ``$request`` object:
101
101
102
102
* ``$request->query`` for ``$_GET``
103
103
* ``$request->request`` for ``$_POST``
@@ -135,7 +135,7 @@ Reading an array of values from $_POST
135
135
<?php
136
136
public function __invoke(Symfony\Component\HttpFoundation\Request $request): Response
137
137
{
138
-
$ids = $request->request->all()["ids"] ?? [];
138
+
$ids = $request->request->get("ids", []);
139
139
}
140
140
141
141
Reading a file
@@ -156,14 +156,16 @@ Single vs multi action controllers
156
156
157
157
The examples in this documentation use the magic ``__invoke`` method to force the controller to have only one action (see https://symfony.com/doc/current/controller/service.html#invokable-controllers).
158
158
159
-
In general, this is recommended way to proceed but we do not force it and you are allowed to use multi actions controllers if you need them.
159
+
In general, this is a recommended way to proceed but we do not force it and you are allowed to use multi actions controllers if you need them, by adding another public method and configuring it with the ``#[Route(...)]`` attribute.
160
160
161
161
Handling errors (missing rights, bad request, …)
162
162
++++++++++++++++++++++++++++++++++++++++++++++++
163
163
164
-
A controller may throw some exceptions if it receive an invalid request.
164
+
A controller may throw some exceptions if it receive an invalid request. Exceptions will automatically converted to error pages.
165
165
166
-
You can use any exception that extends ``Symfony\Component\HttpKernel\Exception``, see below examples.
166
+
If you need exceptions with specific HTTP codes (like 4xx or 5xx codes), you can use any exception that extends ``Symfony\Component\HttpKernel\Exception\HttpException``.
167
+
168
+
GLPI also provide some custom Http exceptions in the ``Glpi\Exception\Http\`` namespace.
167
169
168
170
Missing rights
169
171
++++++++++++++
@@ -174,7 +176,20 @@ Missing rights
174
176
public function __invoke(Symfony\Component\HttpFoundation\Request $request): Response
175
177
{
176
178
if (!Form::canUpdate()) {
177
-
throw new Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
179
+
throw new \Glpi\Exception\Http\AccessDeniedHttpException();
180
+
}
181
+
}
182
+
183
+
Invalid header
184
+
++++++++++++++
185
+
186
+
.. code-block:: php
187
+
188
+
<?php
189
+
public function __invoke(Symfony\Component\HttpFoundation\Request $request): Response
190
+
{
191
+
if ($request->headers->get('Content-Type') !== 'application/json') {
192
+
throw new \Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException();
178
193
}
179
194
}
180
195
@@ -188,7 +203,7 @@ Invalid input
188
203
{
189
204
$id = $request->request->getInt('id');
190
205
if ($id == 0) {
191
-
throw new Symfony\Component\HttpKernel\Exception\BadRequestHttpException();
206
+
throw new \Glpi\Exception\Http\BadRequestHttpException();
192
207
}
193
208
}
194
209
@@ -208,7 +223,7 @@ Possible responses
208
223
209
224
You may use different responses classes depending on what your controller is doing (sending json content, outputting a file, …).
210
225
211
-
There is also a render helper method that helps you return a rendered twig content as a response.
226
+
There is also a render helper method that helps you return a rendered Twig template as a Response object.
212
227
213
228
Sending JSON
214
229
++++++++++++
@@ -232,7 +247,7 @@ Sending a file from memory
232
247
$filename,
233
248
);
234
249
235
-
$response = new Symfony\Component\HttpFoundation;\Response($file_content);
250
+
$response = new Symfony\Component\HttpFoundation\Response($file_content);
Controller should be *thin*, which mean they should contains the minimal code needed to *glue* together the pieces of GLPI needed to handle the request.
289
+
Controller should be *thin*, which mean they should contain the minimal code needed to *glue* together the pieces of GLPI needed to handle the request.
275
290
276
291
A good controller does only the following actions:
277
292
278
293
* Check the rights
279
294
* Validate the request
280
295
* Extract what it needs from the request
281
296
* Call some methods from a dedicated service class that can process the data (using DI in the future, not possible at this time)
282
-
* Return a response
297
+
* Return a ``Response`` object
283
298
284
299
Most of the time, this will take between 5 and 15 instructions, resulting in a small method.
285
300
@@ -297,9 +312,9 @@ Unless you are making a generic controller that is explicitly made to be extende
297
312
Always restrict the HTTP method
298
313
+++++++++++++++++++++++++++++++
299
314
300
-
If your controller is only meant to be used with a specific HTTP method (e.g. `POST`), it is best to define it.
315
+
If your controller is only meant to be used with a specific HTTP method (e.g. `POST`), it is best to define it in the ``Route`` attribute.
301
316
302
-
It helps others developers to understand how this route must be used and help debugging when miss-using the route.
317
+
It helps others developers understand how this route must be used and help debugging when misusing the route.
303
318
304
319
.. code-block:: php
305
320
@@ -323,4 +338,42 @@ URL generation
323
338
324
339
Ideally, URLs should not be hard-coded but should instead be generated using their route names.
325
340
326
-
This is not yet possible in many places so we have to rely on hard-coded urls at this time.
341
+
In your Controllers, you can inject the Symfony router in the constructor in order to generate URLs based on route names:
342
+
343
+
.. code-block:: php
344
+
345
+
<?php
346
+
namespace Glpi\Controller\Custom;
347
+
348
+
use Glpi\Controller\AbstractController;
349
+
use Symfony\Component\HttpFoundation\Request;
350
+
use Symfony\Component\HttpFoundation\Response;
351
+
use Symfony\Component\Routing\Attribute\Route;
352
+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
353
+
354
+
class MyController extends AbstractController
355
+
{
356
+
public function __construct(
357
+
private readonly UrlGeneratorInterface $router
358
+
) {
359
+
}
360
+
361
+
public function __invoke(Request $request): Response
0 commit comments