// BLOG

Kirby CMS - Les routes

La notion de route dans les applications web correspond à l'adresse qu'il faufra indiquée pour arriver à bonne destination, en l'occurence une page web. Kirby étant par défaut en mode "Flat file", la notion de route à toute son importance. Vous n'avez généralement pas à vous souciez du routing des pages dans Kirby qui le gère de manière transparente. Cependant, dans certains cas particulier, vous pourriez être amené à définir vos propres routes.

Rédigé le 14.05.2022
Par Gilles Vauvarin

Kirby CMS PHP Route

Quand définir nos propres routes ?

Kirby gère automatiquement les routes des pages que vous crééez depuis le panel.

Emplacement physique URL d'accès
/content/posts/first-post/post.txt https://my-website.com/posts/first-post

Imaginons maintenant que vous souhaitiez accéder à une page qui affiche des données qui ne proviennent pas d'un fichier texte contenu dans le dossier /content (flux RSS, Sitemap, API, fichiers de données ...) ou que vous souhaitiez déclencher l'execution d'un programme en tappant une URL (génération d'un PDF ou d'un CSV ...). Dans ces cas, vous aurez besoin de définir vous même une route personnalisée dans le fichier de configuration de Kirby. Les cas sont nombreux où vous pourriez avoir besoin de créer vos propres routes, il est donc important de comprendre comment les implémenter dans votre projet Kirby.

Comment définir nos propres routes ?

Vous pouvez définir des routes personnalisées soit dans le fichier de configuration de Kirby qui se trouve dans /site/config/config.php soit dans un plugin. Je n'aborderai pas la façon dont ont définie une route personnalisée dans un plugin, je vous renvoie vers la documentation officielle.

Le système de routing est déjà implémenté dans Kirby. Pour créer une route personnalisée, il suffit d'étendre ce système de routing à l'aide d'un tableau associatif dans lequel on passe des valeurs sur des mots clés prédéfinis qui sont routes , pattern et action.

Voici à quoi cela va ressembler dans votre fichier de configuration :

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => 'mon/chemin/custom',
      'action'  => function () {
        // le code à executer lorsque l'URL correspondante est appelée
      }
    ],
  ]
];

Vous pouvez biensure ajouter autant de routes que vous le souhaitez dans le tableau 'routes' => []

Les patterns

La valeur passée à la clé pattern peut être une chaîne de caractère indiquant une URL relative comme dans l'exemple précédent 'pattern' => 'mon/chemin/custom' mais vous pouvez également utiliser des "placeholders" dynamiques. Ceux-ci permettent d'indiquer un format générique d'URL qui répondent à plusieurs expressions de route. Voyez ces placeholders dynamiques comme des Regex (expressions régulières). Kirby nous en propose quelque une prêt à l'emploi :

Placeholder Description
(:all) Prend en compte tous les caractères, y compris le slash /
(:alpha) Prend en compte tous les caractères alphabétiques entre a-z et A-Z
(:alphanum) Prend en compte tous les caractères alphanumériques entre a-z, A-Z et 0-9
(:any) Prend en compte tous types de caractères jusqu'au prochain slash /
(:num) Prend en compte tous les nombres

Vous pouvez également passer votre propre expression régulière [a-z]+

Voici un exemple d'utilisation des placeholders dynamiques dans la définition d'un pattern de route :

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => 'mon/chemin/(:any)/([a-z]+)',
      'action'  => function () {
        // le code à executer lorsque l'URL correspondante est appelée
      }
    ],
  ]
];

La fonction de callback passée sur l'option action peut prendre en argument les placeholders dynamiques à condition de les passer dans l'ordre dans lequel ils sont utilisés :

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => 'mon/chemin/(:any)/(:num)/(:all)',
      'action'  => function($any, $num, $all) {
        // le code à executer lorsque l'URL correspondante est appelée
      }
    ],
  ]
];

Si vous souhaitez utiliser la même action pour plusieurs patterns différents, vous pouvez soit utiliser des expressions régulières, soit passer un tableau de patterns :

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => ['posts/(:any)', 'projects/(:any)', 'products/(:num)'],
      'action'  => function() {
        // le code à executer lorsque l'URL correspondante est appelée
      }
    ],
  ]
];

Les différents types de réponses

Selon les cas, la fonction de callback passée à la clé action peut retourner différents types de résultats qui seront traitées en conséquence par Kirby.

Il peut sagir d'un objet Page Pages User Users File Collection... et tout type d'objet Kirby. Il peut aussi sagir d'une chaine de caractères avec ou sans balises HTML, d'une valeur null, vide ou booléen, un tableau de valeurs qui sera interprété comme un JSON, une Exception ...

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => 'fancy.json',
      'action'  => function () {
        return [
          'status' => 'ok',
          'data'   => ['foo' => 'bar']
        ];
      }
    ],
    [
      'pattern' => 'index.html',
      'action'  => function () {
        return '<html><body>Hello word !</body></html>';
       }
    ],
    [
      'pattern' => 'mon/chemin',
      'action'  => function () {
         throw new Exception('Alert !!!');
      }
    ]
  ]
];

Les méthodes de requêtes

Par défaut les routes dans Kirby sont uniquement disponibles pour des requêtes de type GET mais vous pouvez préciser explicitement d'autres types de requêtes via la clé method selon vos besoins :

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => 'mon/chemin',
      'action'  => function() {
        // le code à executer lorsque l'URL correspondante est appelée
      },
      'method' => 'GET|POST|DELETE'
    ],
  ]
];

Voici les types de requêtes disponibles pour vos routes :

CONNECT DELETE GET HEAD OPTIONS PATCH POST PUT TRACE

La méthode next()

Il y a des scénarii où vous pourriez avoir besoin de déterminer si une URL appelée correspond à une certaine catégorie de pages et dans ce cas executer un script. Pour cela on va utiliser le pattern (:any) pour intercepter toutes les URL et filtrer ces URLs selon une condition. Si la condition est vraie, on execute le script voulu, si pas, on veut que Kirby nous affiche la page. Cela est possible grâce à l'instruction $this->next()

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => '(:any)',
      'action'  => function($any) {
           if ( page($any)->template() == 'post' ) :
             // le code à executer
             return $result;
           endif;
           $this->next();
      },
    ],
  ]
];

Dans le code précédent :

'pattern' => '(:any)' : On intercepte toutes les URLs.

if ( page($any)->template() == 'post' ) : On vérifie si la page est de type 'post', si oui on execute notre code et on renvoie un résultat.

$this->next() : Si la condition n'est pas remplie (la page appelée n'est pas de type 'post'), kirby affiche la page en question sans executer quoi que ce soit.

Passer des données au controller ou au template

Depuis une route, vous pouvez passer un tableau de données dont chaque élément sera considéré comme une variable globale et donc accessible depuis le controller et/ou le template de la page que vous retournez, si c'est un objet Page que vous avez décidé de retourner dans fonction de callback.

Pour cela, il faut utiliser la méthode render()

// /site/config/config.php

return [
  'routes' => [
    [
      'pattern' => '(:any)',
      'action'  => function($any) {
          if ( page($any)->template() == 'post' ) :
             $data = [
                 'slug' => $any,
             ];
             return page('posts')->render($data);
          endif;
          $this->next();
      },
    ],
  ]
];

Dans le code précédent, je renvoie la page posts et lui associe un tableau dans lequel se trouve le slug de la page qui vient d'être appelée. La variable $slug sera alors disponible dans le controller de la page posts:

<?php

// /site/controllers/posts.php

return function ($page, $slug) {

  // Code de mon controller ... qui a accès à la valeur de $lug

    return [
        'post_title' => page($slug)->title()
    ];

};

... mais aussi dans le template correspondant si besoin :

<?php

// /site/templates/posts.php

<h2><a href="<?= page($slug)->url() ?>">$post_title</a></h2>

Le hook "route"

Vous disposez avec Kirby d'un hook "route" avec les deux états route:before et route:after

Vous pouvez donc executer un code personnalisé juste avant qu'une page (avec un pattern d'URL particulier ou pas) soit appelée ou juste après.

Exemple :

// /site/config/config.php

return [
  'hooks' => [
    'route:before' => function ($route, $path, $method) {
      if ( $path === 'super/secret' && !kirby()->user() ) {
        die();
      }
    }
  ]
];

Dans le code précédent :

'route:before' => function ($route, $path, $method) { : On surveille lorsqu'une page est appelée et on execute un code juste avant que la page soit renvoyée par Kirby.

if ( $path === 'super/secret' && !kirby()->user() ) { : On teste que la page appelée soit https://monsite.com/super/secret et on teste si l'utilisateur est connecté.

Si c'est bien cette page et que l'utilisateur n'est pas connecté, on lui refuse l'accès. Un moyen simple de restreindre l'accès à certaines pages de votre front-end.

Route et page virtuelle

Les routes peuvent aussi retourner, ce que l'on appelle dans Kirby, des pages virtuelles c'est à dire des pages qui n'existent pas physiquement dans le système de fichier du dossier /content mais qui sont alimentés par des données provenant de sources externes. Je ne vais pas en dire plus sur les pages virtuelles, celles-ci faisant l'objet d'un article à part entière.

Accedéder aux paramètres et "query string"

Les paramètres et les query string ne peuvent pas être indiqués au niveau du pattern de la route. Cependant ils peuvent être récupérés à l'aide des méthodes "helpers" param() et get() de Kirby.

// /site/config/config.php

<?php

return [
    'routes' => [
        [
            'pattern' => 'products/(:any)',
            'action'  => function($uid) {
                // parameter, e.g. `https://example.com/products/color:black`
                if ($param = param('color')) {
                  // do something
                }
                // query string, e.g. `https://example.com/products?color=black`
                if ($query = get('color')) {
                  // do something
                }
                // ...
            }
        ],
    ]
];

La vidéo YouTube