4min.

Une bonne convention de nommage pour les routes, les contrôleurs et les templates ?

This blog post is also available in 🇬🇧 English: A Good Naming Convention for Routes, Controllers and Templates?.

J’ai participé à de nombreux projets web au cours de ma carrière, et avec l’expérience accumulée, j’insiste de plus en plus sur les normes de codage, les conventions et la précision des noms dans mes revues de code.

Un service mal nommé, une variable qui ne donne aucun indice sur ce qu’elle contient, un namespace qui n’a aucun sens, plusieurs noms pour le même concept : tout cela rend la lecture du code plus difficile et demande un effort cognitif plus important que ce que je peux me permettre 🤣 C’est ce qu’on appelle la dette visuelle.

Aujourd’hui, je veux écrire sur le nommage dans un framework web standard route/contrôleur/modèle (les exemples seront Symfony mais cela s’applique à Laravel, Django, etc).

Partons de ce besoin :

  1. Nous avons besoin d’afficher une liste de produits sur /products ;
  2. Nous devons afficher un produit sur /product/{sku} ;
  3. Nous utilisons un framework MVC.

Quels noms utiliser pour :

  • le nom de la route (la chaîne que nous pouvons utiliser pour générer l’URL de nos pages) ;
  • les classes et méthodes du contrôleur et de l’action ;
  • les fichiers de template.

Les frameworks nous permettent de choisir, et quand nous laissons place à l’imagination, cela peut devenir très vite désordonné.

Section intitulée le-desordre-que-nous-sommes-autorises-a-faireLe désordre que nous sommes autorisés à faire 💩

J’ai vu de tout, regardons un MAUVAIS exemple :

<?php

class AppController extends AbstractController
{
    #[Route('/products', name: 'all_products')]
    public function listProducts(): Response
    {
        return $this->render('product/list.html.twig', [
            'products' => [],
        ]);
    }
}

Le contrôleur App a une route all_products, attachée à une action listProducts, rendant un template product/list.html.twig – mes yeux saignent.

Une autre appellation que je vois très souvent est d’avoir un dossier de template par action, comme ceci : listProducts/index.html.twig. Nous avons tellement de index.html.twig que nous ne cherchons plus nos templates par leurs noms, et si plusieurs contrôleurs ont les mêmes noms d’actions… eh bien… Vous pouvez aussi avoir des soucis dans votre éditeur de code avec tous les fichiers ouverts qui ont le même nom (merci à PHPStorm d’être intelligent à ce sujet, il ajoute le nom du dossier à l’onglet si vous ouvrez deux index.html.twig).

Section intitulée un-meilleur-nommageUn meilleur nommage 🙏

Ce que je suggère maintenant dans mes projets, c’est d’avoir un nommage cohérent comme celui-ci :

  • Le contrôleur est nommé en fonction du métier, il peut être namespacé, ne contient que des noms singuliers et est PascalCase 🐫 ;
  • L’action est nommée en fonction de ce qu’elle fait, un verbe en camelCase 🐫 – nous essayons également d’utiliser des noms cohérents entre les différents contrôleurs ;
  • Le nom de la route est une concaténation du nom du contrôleur et du nom de l’action, transformés en snake_case 🐍 ;
  • Le dossier des templates est le nom du contrôleur transformé en snake_case 🐍 ;
  • Le nom du template est le nom de l’action dans le snake_case 🐍.
<?php

class ProductController extends AbstractController
{
    #[Route('/products', name: 'product_list')]
    public function list(): Response
    {
        return $this->render('product/list.html.twig', [
            'products' => [],
        ]);
    }

    #[Route('/product/{sku}', name: 'product_show')]
    public function show(string $sku): Response
    {
        return $this->render('product/show.html.twig', [
            'product' => $product,
        ]);
    }

    #[Route('/product/{sku}/add', name: 'product_add_to_cart')]
    public function addToCart(string $sku): Response
    {
        return $this->render('product/add_to_cart.html.twig', [
            'product' => $product,
        ]);
    }
}

De cette façon, lorsque je regarde les templates, je sais dans quel contrôleur ils sont utilisés. De même, lorsque je regarde un nom de route (à partir d’APM ou de logs, ou même dans un template), je sais instantanément quel contrôleur ils invoquent.

Section intitulée utiliser-le-nom-de-l-action-comme-nom-de-templateUtiliser le nom de l’action comme nom de template ?

J’utilise snake_case 🐍 pour mes templates pour suivre les recommandations Symfony mais plus j’y pense, plus j’ai envie de me débarrasser de ces transformations entre la camelCase 🐫 de l’action et la snake_case 🐍 du template. Cela peut donc être plus simple / plus facile à utiliser :

#[Route('/product/{sku}/add', name: 'product_add_to_cart')]
public function addToCart(string $sku): Response
{
    return $this->render('product/addToCart.html.twig', [
        'product' => $product,
    ]);
}

Qu’en pensez-vous ?

Section intitulée des-noms-de-route-en-camelcaseDes noms de route en camelCase ?

Une autre transformation que nous faisons est sur le nom de la route. À partir de Product et addToCart, nous créons product_add_to_cart.

Mais la plupart des frameworks autorisent n’importe quelle chaîne de caractères comme nom de route. Peut-être pourrions-nous utiliser Product_addToCart ?

Je n’aime pas ça visuellement, mais c’est peut-être plus simple à écrire !

Section intitulée utiliser-invoke-adr-patternUtiliser __invoke() / ADR pattern

Dans la communauté Symfony, ce pattern est assez commun. Et un effet secondaire amusant est que le nom de la route peut être directement self::class (le nom de classe pleinement qualifié du contrôleur) :

<?php

#[Route('/product/{sku}', name: self::class)]
class ProductShow
{
    public function __invoke(string $sku, Environment $twig): Response
    {
        return new Response(
            $twig->render('productShow/index.html.twig', ['product' => $product,])
        );
    }
}

Cela permet de générer des urls de ce type, ce qui est plutôt agréable :

$url = $this->urlGenerator->generate(ProductShow::class) ;

Section intitulée et-vousEt vous ?

J’ai besoin de savoir si nous pouvons améliorer / trouver une bonne convention. Je ne dis pas que celle-ci est parfaite, toute convention est bonne si elle est appliquée partout et respectée.

Mais disons qu’une convention fait l’unanimité chez les développeurs, nous pourrions même l’ajouter à des outils d’analyse statique – une extension pour phpstan ou Psalm par exemple, pourrait regarder vos noms de routes / actions / templates et vous faire la morale si vous vous trompez 😍.

N’hésitez pas à me faire part de vos remarques, avez-vous une convention ? Qu’est-ce que vous utilisez ? En êtes-vous satisfait ? Faites-le-moi savoir !

La prochaine fois, nous devrions parler des alias de Commandes par rapport aux noms de classes et aux conventions d’espace de noms ? 😀

Commentaires et discussions

Nos formations sur ce sujet

Notre expertise est aussi disponible sous forme de formations professionnelles !

Voir toutes nos formations