DEV Community

Guillaume
Guillaume

Posted on

CMS en Symfony : le routing

Maintenant qu'on a regardé un peu la partie fonctionnelle, on va faire un peu de technique.

L'une des fonctionnalités à laquelle chaque utilisateur de CMS s'attend à trouver, est la gestion du "routing" : "je crée une page "Actualités" et je m'attends à pouvoir l'afficher si je tape "www.monsupersite.com/actualites".

2 choses à gérer :

  • pouvoir créer des pages
  • créer un "slug" pour chaque page
  • faire en sorte que chaque page soit automatiquement enregistrée dans les routes du CMS pour les rendre disponibles.

On va donc commencer par ... la fin (je laisse la structure des pages pour le prochain épisode !).

Evidemment, notre framework préféré nous donne la possibilité de faire ça en ... 5 minutes chrono !

Routing as a Service.

Quand on utilise Symfony, on a l'habitude d'utiliser:

  • soit le fichier yaml de routing (basique, mais ça sert parfois, valable aussi en XML ou en PHP)
  • soit les annotations dans les controller (ou les attributs maintenant, hein les dinosaures ! )

Evidemment, que ce soit l'un ou l'autre, ça ne répond pas à notre besoin : on a besoin de quelque chose de dynamique qui va cherche la liste de nos pages en base de données et qui les "charge".

Hé bien c'est exactement ce que Symfony nous propose !

On appelle ça un "route loader". Pour les curieux (et parce que je vous invite à lire la doc !) c'est ici.

Nous, on a une contrainte supplémentaire : certes, on crée un CMS, mais on veut aussi pouvoir faire du Symfony "natif". En bref, si je veux créer une route "hors CMS", je dois pouvoir le faire.

Qu'à cela ne tienne, on a aussi cette possibilité.

Le fichier de définition des routes

Dans Symfony, le fichier de base pour définir les routes est le fichier "config/routes.yaml".

Par défaut, dans une application Symfony (version 6) il doit ressembler à quelque chose du genre :

controllers:
    resource: ../src/Controller/
    type: annotation

kernel:
    resource: ../src/Kernel.php
    type: annotation
Enter fullscreen mode Exit fullscreen mode

On va ajouter quelques lignes en haut du fichier tel que:

cms_routes:
    resource: .
    type: cms

controllers:
    resource: ../src/Controller/
    type: annotation

kernel:
    resource: ../src/Kernel.php
    type: annotation
Enter fullscreen mode Exit fullscreen mode

On ajoute un nouveau "groupe de routes" tel que:

  • cms_routes : nom arbitraire. Mettez "toto24" si ça vous fait plaisir ;)
  • resource: . : en fait on ignore ce paramètre, d'où le point
  • type: cms : heu c'est pas natif ce type ? Non, c'est pas natif ! C'est là où on intervient : on va aller créer notre loader, responsable de ce type de route.

Let's go.

A votre service :-)

Pour cela dans le répertoire "src" à la racine de votre projet, créez un répertoire "Routing", puis créez dans ce dernier une classe "CmsRouteLoader".

Voici le code:


namespace Routing;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection

class CmsRouteLoader extends Loader
{
    private $isLoaded = false;

    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {

        $this->entityManager = $entityManager;
    }

    public function load($resource, string $type = null): RouteCollection
    {
        if (true === $this->isLoaded) {
            throw new \RuntimeException('Do not add the "CMS" loader twice');
        }

        $routes = new RouteCollection();

        $nodes = $this->entityManager->getRepository(Page::class)->findAll();
        foreach ($nodes as $node) {
            $defaults = [
                '_controller' => 'App\Controller\CmsController::slugs'
            ];
            $route = new Route($node->getPath(), $defaults);
            $routes->add('cms_slugs_' . $node->getId(), $route);
        }

        $this->isLoaded = true;
        return $routes;
    }

    public function supports($resource, string $type = null)
    {
        return 'cms' === $type;
    }

}
Enter fullscreen mode Exit fullscreen mode

Quelques explications (notez que notre classe ressemble beaucoup à la documentation):

  • notre classe étend Loader, ce qui va nous permettre de définir une méthode "supports". Vous remarquerez qu'on retourne true si le type du groupe est "cms"....ça vous rappelle le fichier de config ?
  • ici la classe est un peu simplifiée, mais en bref, on charge les pages de la base, et pour chaque page on crée dynamiquement une route qu'on ajoute à son tour dans une collection de routes qu'on retourne.

Enfin, on enregistre notre service dans les services de l'application en modifiant le fichier config/services.yaml comme suit:

# config/services.yaml
services:
    # ...

    App\Routing\CmsRouteLoader:
        tags: [routing.loader]
Enter fullscreen mode Exit fullscreen mode

On fera quelques modifications dans le prochain chapitre pour mettre en place nos structures pour enregistrer nos pages et autres dans la base de données.

Il manque aussi le controller 'App\Controller\CmsController' mais pour l'instant on insiste pas, on s'y intéressera plus tard.

Bientôt la suite !

Latest comments (1)

Collapse
 
slowwie profile image
Michael

In fact, it is often not even necessary to set up a router loader.
I mean, how many "really" distinct pages does the average company page have? Aboutpage, History, Team, Products, Categories, Blog, Contact us and Frontpage. For such sites I even prefer to use native symfony routing. That gets you very far.
You also have to take into account that you want to offer multilingual pages and multisite even for small to medium-sized company websites. Symfony routing can do it all. As soon as you start with a RouteLoader, it starts getting complex... for example think of generating sitemaps, that's super easy if you omit a custom RouteLoader as long as you can.