Search code examples
phpsymfonyannotationssymfony5symfony-routing

How to provide Symfony routing parameter programatically?


In this Symfony route

/**
 * @Route("/board/{board}/card/{card}", name="card_show", methods={"GET"}, options={})
 */
public function show(Board $board, Card $card): Response
{
    $card->getLane()->getBoard(); // Board instance
    // ...
}

How is it possible to add the {board} parameter programatically, since it is already available in {card}? Now, I always need to add two parameters, when generating links to show action.

After some research I've found the RoutingAutoBundle (https://symfony.com/doc/master/cmf/bundles/routing_auto/introduction.html#usage) which would provide the functions I need, but it's not available for Symfony 5 anymore.

Thanks.


Solution

  • Okay, after some investigation I've found this question Which lead me to this helpful answer.

    My controller action (with @Route annotation) looks like this:

    /**
     * @Route("/board/{board}/card/{card}", name="card_show", methods={"GET"})
     */
    public function show(Card $card): Response
    {
    }
    

    We just have one argument ($card) in method signature, but two arguments in route.

    This is how to call the route in twig:

    path("card_show", {card: card.id})
    

    No board parameter required, thanks to a custom router.

    This is how the custom router looks like:

    <?php // src/Routing/CustomCardRouter.php
    
    namespace App\Routing;
    
    use App\Repository\CardRepository;
    use Symfony\Component\Routing\RouterInterface;
    
    class CustomCardRouter implements RouterInterface
    {
        private $router;
        private $cardRepository;
    
        public function __construct(RouterInterface $router, CardRepository $cardRepository)
        {
            $this->router = $router;
            $this->cardRepository = $cardRepository;
        }
    
        public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
        {
            if ($name === 'card_show') {
                $card = $this->cardRepository->findOneBy(['id' => $parameters['card']]);
                if ($card) {
                    $parameters['board'] = $card->getLane()->getBoard()->getId();
                }
            }
            return $this->router->generate($name, $parameters, $referenceType);
        }
    
        public function setContext(\Symfony\Component\Routing\RequestContext $context)
        {
            $this->router->setContext($context);
        }
    
        public function getContext()
        {
            return $this->router->getContext();
        }
    
        public function getRouteCollection()
        {
            return $this->router->getRouteCollection();
        }
    
        public function match($pathinfo)
        {
            return $this->router->match($pathinfo);
        }
    }
    

    Now, the missing parameter board is provided programatically, by injecting and using the card repository. To enable the custom router, you need to register it in your services.yaml:

    App\Routing\CustomCardRouter:
        decorates: 'router'
        arguments: ['@App\Routing\CustomCardRouter.inner']