Skip to content

Commit 287aa12

Browse files
Pierstovaltrasher
authored andcommitted
Update controllers docs
1 parent 54d25be commit 287aa12

File tree

1 file changed

+76
-23
lines changed

1 file changed

+76
-23
lines changed

source/devapi/controllers.rst

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ You need a `Controller` any time you want an URL access.
55

66
.. note::
77

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.
99

1010
.. warning::
1111

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.
1313

14-
Any new feature added to GLPI >= 11 **must** use `Controllers`.
14+
Any new feature added to GLPI >=11 **must** use `Controllers`.
1515

1616
Creating a controller
1717
^^^^^^^^^^^^^^^^^^^^^
1818

1919
Minimal requirements to have a working controller:
2020

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``.
2323
* The controller must extends the ``Glpi\Controller\AbstractController`` class.
2424
* The controller must define a route using the Route attribute.
2525
* The controller must return some kind of response.
@@ -76,7 +76,7 @@ Dynamic route parameter
7676

7777
.. code-block:: php
7878
79-
#[Symfony\Component\Routing\Attribute\Route("/Ticket/{$id}", name: "glpi_ticket")]
79+
#[Symfony\Component\Routing\Attribute\Route("/Ticket/{id}", name: "glpi_ticket")]
8080
8181
Restricting a route to a specific HTTP method
8282
+++++++++++++++++++++++++++++++++++++++++++++
@@ -97,7 +97,7 @@ You will thus need to prefix your route by ``/ajax`` until we find a better way
9797
Reading query parameters
9898
^^^^^^^^^^^^^^^^^^^^^^^^
9999

100-
These parameters are found in the $request object:
100+
These parameters are found in the ``$request`` object:
101101

102102
* ``$request->query`` for ``$_GET``
103103
* ``$request->request`` for ``$_POST``
@@ -135,7 +135,7 @@ Reading an array of values from $_POST
135135
<?php
136136
public function __invoke(Symfony\Component\HttpFoundation\Request $request): Response
137137
{
138-
$ids = $request->request->all()["ids"] ?? [];
138+
$ids = $request->request->get("ids", []);
139139
}
140140
141141
Reading a file
@@ -156,14 +156,16 @@ Single vs multi action controllers
156156

157157
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).
158158

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.
160160

161161
Handling errors (missing rights, bad request, …)
162162
++++++++++++++++++++++++++++++++++++++++++++++++
163163

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.
165165

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.
167169

168170
Missing rights
169171
++++++++++++++
@@ -174,7 +176,20 @@ Missing rights
174176
public function __invoke(Symfony\Component\HttpFoundation\Request $request): Response
175177
{
176178
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();
178193
}
179194
}
180195
@@ -188,7 +203,7 @@ Invalid input
188203
{
189204
$id = $request->request->getInt('id');
190205
if ($id == 0) {
191-
throw new Symfony\Component\HttpKernel\Exception\BadRequestHttpException();
206+
throw new \Glpi\Exception\Http\BadRequestHttpException();
192207
}
193208
}
194209
@@ -208,7 +223,7 @@ Possible responses
208223

209224
You may use different responses classes depending on what your controller is doing (sending json content, outputting a file, …).
210225

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.
212227

213228
Sending JSON
214229
++++++++++++
@@ -232,7 +247,7 @@ Sending a file from memory
232247
$filename,
233248
);
234249
235-
$response = new Symfony\Component\HttpFoundation;\Response($file_content);
250+
$response = new Symfony\Component\HttpFoundation\Response($file_content);
236251
$response->headers->set('Content-Disposition', $disposition);
237252
$response->headers->set('Content-Type', 'text/plain');
238253
return $response
@@ -243,16 +258,16 @@ Sending a file from disk
243258
.. code-block:: php
244259
245260
<?php
246-
$file = 'path/to/file.txt';
247-
return new Symfony\Component\HttpFoundation\BinaryFileResponse($file);
261+
$file_path = 'path/to/file.txt';
262+
return new Symfony\Component\HttpFoundation\BinaryFileResponse($file_path);
248263
249264
Displaying a twig template
250265
++++++++++++++++++++++++++
251266

252267
.. code-block:: php
253268
254269
<?php
255-
return $this->render('/path/to/my/template.html.twig', [
270+
return $this->render('path/to/my/template.html.twig', [
256271
'parameter_1' => 'value_1',
257272
'parameter_2' => 'value_2',
258273
]);
@@ -271,15 +286,15 @@ General best practices
271286
Use thin controllers
272287
++++++++++++++++++++
273288

274-
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.
275290

276291
A good controller does only the following actions:
277292

278293
* Check the rights
279294
* Validate the request
280295
* Extract what it needs from the request
281296
* 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
283298

284299
Most of the time, this will take between 5 and 15 instructions, resulting in a small method.
285300

@@ -297,9 +312,9 @@ Unless you are making a generic controller that is explicitly made to be extende
297312
Always restrict the HTTP method
298313
+++++++++++++++++++++++++++++++
299314

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.
301316

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.
303318

304319
.. code-block:: php
305320
@@ -323,4 +338,42 @@ URL generation
323338

324339
Ideally, URLs should not be hard-coded but should instead be generated using their route names.
325340

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
362+
{
363+
$route_name = $this->router->generate('my_route');
364+
365+
// ...
366+
}
367+
}
368+
369+
You can also do it in Twig templates, using the ``url()`` or ``path()`` functions:
370+
371+
.. code-block:: twig
372+
373+
{{ path('my_route') }} {# Shows the url like "/my_route" #}
374+
{{ url('my_route') }} {# Shows the url like "http://localhost/my_route" #}
375+
376+
Check out the Symfony documentation for more details about these functions:
377+
378+
* ``url()`` https://symfony.com/doc/current/reference/twig_reference.html#url
379+
* ``path()`` https://symfony.com/doc/current/reference/twig_reference.html#path

0 commit comments

Comments
 (0)