Routing and controllers

โ† Routing and controllers

Ashterix

docs(articles): English translation of From Magento to Symfony

en/articles/magento-to-symfony/core/routing.md new file +103 โˆ’0

1 + # Routing and controllers
2 +
3 + **Magento** ยท routes.xml + Controller/Question/View.php
4 +
5 + ```xml
6 + <router id="standard">
7 + <route id="faq" frontName="faq">
8 + <module name="Vendor_Faq"/>
9 + </route>
10 + </router>
11 + ```
12 + ```php
13 + class View implements HttpGetActionInterface
14 + {
15 + public function __construct(
16 + private RequestInterface $request,
17 + private QuestionRepositoryInterface $questions,
18 + private PageFactory $pageFactory,
19 + ) {}
20 +
21 + public function execute(): ResultInterface
22 + {
23 + $id = (int) $this->request->getParam('id');
24 + $question = $this->questions->getById($id);
25 + $page = $this->pageFactory->create();
26 + $page->getConfig()->getTitle()->set($question->getTitle());
27 + return $page;
28 + }
29 + }
30 + ```
31 +
32 + **Symfony** ยท src/Controller/FaqController.php
33 +
34 + ```php
35 + use Symfony\Bridge\Doctrine\Attribute\MapEntity;
36 + use Symfony\Component\Routing\Attribute\Route;
37 +
38 + final class FaqController extends AbstractController
39 + {
40 + #[Route('/faq', name: 'faq_index', methods: ['GET'])]
41 + public function index(QuestionRepository $questions): Response
42 + {
43 + return $this->json($questions->findActive());
44 + }
45 +
46 + #[Route('/faq/{slug}', name: 'faq_show', methods: ['GET'])]
47 + public function show(
48 + #[MapEntity(mapping: ['slug' => 'slug'])]
49 + Question $question,
50 + ): Response
51 + {
52 + return $this->json($question);
53 + }
54 + }
55 + ```
56 +
57 + - The entity is resolved from the route parameter; if it doesn't exist, you get a 404 without writing any code. For `{id}` you don't need the `#[MapEntity]` attribute.
58 + - You can request services directly in action arguments, not only in the constructor.
59 + - URLs are generated by route name: `$this->generateUrl('faq_show', ['slug' => $q->getSlug()])`.
60 + - `bin/console debug:router` lists all routes, `router:match /faq/returns` shows which route will match.
61 +
62 + ## JSON input: instead of getParam()
63 +
64 + **Symfony** ยท DTO from the request body with validation
65 +
66 + ```php
67 + final class CreateQuestion
68 + {
69 + public function __construct(
70 + #[Assert\NotBlank, Assert\Length(max: 255)]
71 + public readonly string $title,
72 + #[Assert\NotBlank]
73 + public readonly string $answer,
74 + ) {}
75 + }
76 +
77 + #[Route('/api/questions', methods: ['POST'])]
78 + public function create(
79 + #[MapRequestPayload]
80 + CreateQuestion $input,
81 + QuestionService $questions,
82 + ): JsonResponse
83 + {
84 + $question = $questions->create($input);
85 + return $this->json(['id' => $question->getId()], 201);
86 + }
87 + ```
88 +
89 + Invalid JSON or violated constraints produce a 422 response with a list of errors. For the query string there are `#[MapQueryString]` and `#[MapQueryParameter]`, for the current user there is `#[CurrentUser]`.
90 +
91 + ## Area โ†’ prefix and firewall
92 +
93 + **Symfony** ยท config/routes.yaml
94 +
95 + ```yaml
96 + admin:
97 + resource: ../src/Controller/Admin/
98 + type: attribute
99 + prefix: /admin
100 + name_prefix: admin_
101 + ```
102 +
103 + Access to `/admin` is restricted in `security.yaml` (see the security section). There is no separate DI config per area: if the admin panel needs a different implementation of a service, those are two different services.