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

Routing and controllers

Magento · routes.xml + Controller/Question/View.php

<router id="standard">
    <route id="faq" frontName="faq">
        <module name="Vendor_Faq"/>
    </route>
</router>
class View implements HttpGetActionInterface
{
    public function __construct(
        private RequestInterface $request,
        private QuestionRepositoryInterface $questions,
        private PageFactory $pageFactory,
    ) {}

    public function execute(): ResultInterface
    {
        $id = (int) $this->request->getParam('id');
        $question = $this->questions->getById($id);
        $page = $this->pageFactory->create();
        $page->getConfig()->getTitle()->set($question->getTitle());
        return $page;
    }
}

Symfony · src/Controller/FaqController.php

use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\Routing\Attribute\Route;

final class FaqController extends AbstractController
{
    #[Route('/faq', name: 'faq_index', methods: ['GET'])]
    public function index(QuestionRepository $questions): Response
    {
        return $this->json($questions->findActive());
    }

    #[Route('/faq/{slug}', name: 'faq_show', methods: ['GET'])]
    public function show(
        #[MapEntity(mapping: ['slug' => 'slug'])]
        Question $question,
    ): Response
    {
        return $this->json($question);
    }
}
  • 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.
  • You can request services directly in action arguments, not only in the constructor.
  • URLs are generated by route name: $this->generateUrl('faq_show', ['slug' => $q->getSlug()]).
  • bin/console debug:router lists all routes, router:match /faq/returns shows which route will match.

JSON input: instead of getParam()

Symfony · DTO from the request body with validation

final class CreateQuestion
{
    public function __construct(
        #[Assert\NotBlank, Assert\Length(max: 255)]
        public readonly string $title,
        #[Assert\NotBlank]
        public readonly string $answer,
    ) {}
}

#[Route('/api/questions', methods: ['POST'])]
public function create(
    #[MapRequestPayload]
    CreateQuestion $input,
    QuestionService $questions,
): JsonResponse
{
    $question = $questions->create($input);
    return $this->json(['id' => $question->getId()], 201);
}

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

Area → prefix and firewall

Symfony · config/routes.yaml

admin:
    resource: ../src/Controller/Admin/
    type: attribute
    prefix: /admin
    name_prefix: admin_

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.