Comment avoir un paramètre d’URL booléen avec le routeur Symfony ?

Clément Michelet 5 minutes de lecture
Ordinateur sur un bureau avec un environnement de développement ouvert et un cactus posé sur le bureau
Crédits: James Harrison @ Unsplash

Je vois régulièrement cette question être posée sur le Slack communautaire de Symfony : Comment puis-je récupérer un paramètre d’URL directement en booléen avec le routeur Symfony ?

Je vais vous présenter une approche simple et pratique que je mets souvent en œuvre afin de répondre à cette question.

Déclarer explicitement le paramètre d'URL et convertir manuellement

L’approche la plus naïve à cette problématique est de passer la valeur 0/1 ou false/true dans l’URL et de convertir ensuite.

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

final class InvitationController extends AbstractController
{
    #[Route(
      path: '/invitation/{invitationId}/{isAccepted}',
      name: 'api_invitation_change_status',
      methods:['POST'],
      requirements: ['isAccepted' => '0|1|true|false']
    )]
    public function __invoke(int $invitationId, string $isAccepted): Response
    {
        $isAccepted = filter_var($isAccepted, \FILTER_VALIDATE_BOOLEAN);

        // ...
    }
}

Cependant, cette approche nécessite de convertir le paramètre reçu en booléen ou, à défaut, de comparer la valeur 0/1 ou false/true pour connaître l’action choisie.

Alternativement, on peut choisir de passer des verbes d’actions au lieu de 0/1 pour se rapprocher des termes métiers et donc gagner en clarté.

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

final class InvitationController extends AbstractController
{
    #[Route(
      path: '/invitation/{invitationId}/{action}',
      name: 'api_invitation_change_status',
      methods:['POST'],
      requirements: ['action' => 'accept|decline']
    )]
    public function __invoke(int $invitationId, string $action): Response
    {
        $isAccepted = $action === 'accept' ? true : false;

        // ...
    }
}

Comme la méthode précédente, on doit effectuer une conversion supplémentaire afin de pouvoir déterminer le booléen.

Déclarer implicitement le paramètre d'URL

L’approche que je suggère est d’utiliser la notion d’Extra Parameters. On peut définir notre booléen sans avoir à le déclarer explicitement dans la configuration de la route.

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

final class InvitationController extends AbstractController
{
    #[Route(
      path: '/invitation/{invitationId}/accept',
      name: 'api_invitation_accept',
      methods:['POST'],
      defaults: ['isAccepted' => true]
    )]
    #[Route(
      path: '/invitation/{invitationId}/decline',
      name: 'api_invitation_decline',
      methods:['POST'],
      defaults: ['isAccepted' => false]
    )]
    public function __invoke(int $invitationId, bool $isAccepted): Response
    {
        // ...
    }
}

Cette approche a plusieurs avantages :

  • Les URL sont plus explicites.

    Au lieu de passer une valeur 0/1 ou false/true dans l’URL ou de passer la valeur dans le corps de la requête, on utilise des termes métiers sans avoir à convertir la valeur.

  • Les noms de routes sont explicites.

    Ils peuvent être facilement référencée dans l’application, par exemple sur les boutons pour refuser/accepter l’invitation.

Néanmoins, la déclaration de Route est plus verbeuse. On doit en déclarer 2 au lieu d'une seule et donc dupliquer les paramètres.

Il est possible de mutualiser les définitions en utilisant les Route Groups pour limiter la duplication.

On pourrait décliner cette approche pour gérer l’équivalent d’une énumération. Cependant, avec la validation des paramètres, cette astuce est moins intéressante surtout si le nombre de valeurs possibles est important.