Routeur

Le routage peut être utilisé en plus du mécanisme par défaut qui associe controller/action/{paramètres} à une url.

Routes dynamiques

Les routes dynamiques sont définies à l’exécution.
Il est possible de les déclarer dans le fichier app/config/services.php.

Important

Les routes dynamiques ne devraient être utilisées que si la situation l’exige :

  • Dans le cas d’une micro-application

  • Si une route doit être définie dynamiquement

Dans tous les autres cas, il est conseillé de déclarer les routes avec des annotations ou attributs, afin de bénéficier du cache.

Callback routes

Les routes Ubiquity les plus basiques sont définies à partir d’une Closure.
Dans le contexte des micro-applications, cette méthode évite de devoir créer un contrôleur.

app/config/services.php
1     use Ubiquity\controllers\Router;
2
3     Router::get("foo", function(){
4             echo 'Hello world!';
5     });

Les routes de type callback peuvent être définies pour toutes les méthodes http :

  • Router::post

  • Router::put

  • Router::delete

  • Router::patch

  • Router::options

Controller routes

Les routes peuvent aussi être définies de manière plus conventionnelle à partir de l’action d’un contrôleur :

app/config/services.php
1     use Ubiquity\controllers\Router;
2
3     Router::addRoute('bar', \controllers\FooController::class,'index');

La méthode FooController::index() sera accessible via l’url /bar.

Dans ce cas, le contrôleur FooController doit hériter de Ubiquity\controllers\Controller ou l’un de ces dérivés, et doit implémenter la méthode index :

app/controllers/FooController.php
1     namespace controllers;
2
3     class FooController extends ControllerBase{
4
5             public function index(){
6                     echo 'Hello from foo';
7             }
8     }

Route par défaut

La route par défaut correspond au path /.
Elle peut être définie en utilisant le path réservé _default

app/config/services.php
1     use Ubiquity\controllers\Router;
2
3     Router::addRoute("_default", \controllers\FooController::class,'bar');

Routes statiques

Les routes statiques sont définies via annotations ou en utilisant les attribut php natifs depuis Ubiquity 2.4.0.

Note

Ces annotations ou attributs ne sont jamais lus à l’exécution.
Il est nécessaire de ré-initialiser le cache du router pour prendre en compte les changements effectués sur les routes.

Création

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6
 7    #[Route('products')]
 8    public function index(){}
 9
10}

La méthode Products::index() sera accessible par l’url /products.

Note

Les slash initial ou terminal sont ignorés dans le path. Les routes suivantes sont donc équivalentes :
  • #[Route('products')]

  • #[Route('/products')]

  • #[Route('/products/')]

Paramètres de routes

Une route peut avoir des paramètres :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6...
 7     #[Route('products/{value}')]
 8     public function search($value){
 9         // $value will equal the dynamic part of the URL
10         // e.g. at /products/brocolis, then $value='brocolis'
11         // ...
12     }
13}

Paramètres optionnels des routes

Une route peut définir des paramètres optionnels, si la méthode associée a des arguments optionnels :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6   ...
 7   #[Route('products/all/{pageNum}/{countPerPage}')]
 8   public function list($pageNum,$countPerPage=50){
 9      // ...
10   }
11}

Route requirements

Il est possible d’ajouter des spécifications sur les variables passées dans l’url via l’attribut requirements.

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6   ...
 7   #[Route('products/all/{pageNum}/{countPerPage}',requirements: ["pageNum"=>"\d+","countPerPage"=>"\d?"])]
 8   public function list($pageNum,$countPerPage=50){
 9      // ...
10   }
11}
La route définie correspond à ces urls :
  • products/all/1/20

  • products/all/5/

mais pas avec celle-ci :
  • products/all/test

Types des paramètres

La déclaration de route prend en compte les types de données passés à l’action, ce qui évite d’ajouter des requirements pour les types simples (int, bool, float).

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6   ...
 7   #[Route('products/{productNumber}')]
 8   public function one(int $productNumber){
 9      // ...
10   }
11}
La route définie correspond à ces urls :
  • products/1

  • products/20

mais pas avec celle-ci :
  • products/test

Valeurs possibles par type de données :
  • int: 1

  • bool: 0 or 1

  • float: 1 1.0

Méthodes http des routes

Il est possible de spécifier la ou les méthodes http associées à une route :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6
 7   #[Route('products',methods: ['get','post'])]
 8   public function index(){}
 9
10}

L’attribut methods peut accepter plusieurs méthodes :
@route("testMethods","methods"=>["get","post","delete"])
#[Route('testMethods', methods: ['get','post','delete'])]

L’annotation @route ou l’attribut Route correspondent à l’ensemble des méthodes http.
Une annotation spécifique existe pour chaque méthode http :

  • @get => Get

  • @post => Post

  • @put => Put

  • @patch => Patch

  • @delete => Delete

  • @head => Head

  • @options => Options

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Get;
 4
 5class ProductsController extends ControllerBase{
 6
 7   #[Get('products')]
 8   public function index(){}
 9
10}

Nom de route

Il est possible de spécifier le nom name d’une route pour faciliter l’accès à l’url associée.
Si l’attribut name n’est pas spécifié, chaque route a un nom par défaut, basé sur le pattern controllerName.methodName.

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6
 7   #[Route('products',name: 'products.index')]
 8   public function index(){}
 9
10}

Génération d’URL

Les routes names peuvent être utilisés pour générer les URLs.

Liens vers une route en Twig

<a href="{{ path('products.index') }}">Products</a>

Route globale

L’annotation @route peut être utilisée sur une classe contrôleur :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5#[Route('products')]
 6class ProductsController extends ControllerBase{
 7   ...
 8   #[Route('/all')]
 9   public function display(){}
10
11}

Dans ce cas, la route définie sur le contrôleur est utilisée en tant que préfixe pour toutes les routes du contrôleur :
La route générée pour l’action display est /product/all

Routes automatiques

Si une route globale est définie, il est possible d’ajouter toutes les actions du contrôleur en tant que routes (en utilisant le préfixe global), en spécifiant le paramètre automated :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5#[Route('/products',automated: true)]
 6class ProductsController extends ControllerBase{
 7
 8   public function index(){}
 9
10   public function generate(){}
11
12   public function display($id){}
13
14}
L’attribut automated définit 3 routes depuis ProductsController :
  • /product/(index/)?

  • /product/generate

  • /product/display/{id}

Routes héritées

Avec l’attribut inherited, il est possible de générer les routes déclarées dans la classe de base, ou de générer automatiquement les routes associées aux actions de la classe de base, si l’attribut automated est mis à true en même temps.

Classe de base :

app/controllers/ProductsBase.php
 1 namespace controllers;
 2
 3 use Ubiquity\attributes\items\router\Route;
 4
 5 abstract class ProductsBase extends ControllerBase{
 6
 7     #[Route('(index/)?')]
 8     public function index(){}
 9
10     #[Route('sort/{name}')]
11     public function sortBy($name){}
12
13 }

La classe dérivée utilisant les membres hérités :

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5#[Route('/product',inherited: true)]
 6class ProductsController extends ProductsBase{
 7
 8   public function display(){}
 9
10}
L’attribut inherited définit les 2 routes de ProductsBase:
  • /products/(index/)?

  • /products/sort/{name}

Si les attributs automated et inherited sont utilisés en conjonction, les actions de la classe de base sont également ajoutées aux routes.

Paramètres globaux de routes

La partie globale d’une route peut définir des paramètres, qui seront passés dans toutes les routes générées.
Ces paramètres peuvent être récupérés par le biais d’un membre de données public :

app/controllers/FooController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5#[Route('/foo/{bar}',automated: true)]
 6class FooController {
 7
 8   public string $bar;
 9
10   public function display(){
11       echo $this->bar;
12   }
13
14}

L’accès à l’url /foo/bar/display affiche le contenu du membre bar.

Routes avec préfixe global

Si la route globale est définie sur un contrôleur, toutes les routes générées dans ce contrôleur sont précédées du préfixe.
Il est possible d’introduire explicitement des exceptions sur certaines routes, en utilisant le préfixe #/.

app/controllers/FooController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5#[Route('/foo',automated: true)]
 6class FooController {
 7
 8   #[Route('#/noRoot')]
 9   public function noRoot(){}
10
11}

Le contrôleur définit l’url /noRoot url, qui n’est pas préfixée par /foo.

Priorité des routes

Le paramètre prority d’une route permet à celle ci d’être résolue avec une plus ou moins grande priorité.

Plus le paramètre de priorité est élevé, plus la route sera définie au début de la pile des routes en cache.

Dans l’exemple ci-dessous, la route produits/all sera définie avant la route /produits.

app/controllers/ProductsController.php
 1namespace controllers;
 2
 3use Ubiquity\attributes\items\router\Route;
 4
 5class ProductsController extends ControllerBase{
 6
 7   #[Route('products', priority: 1)]
 8   public function index(){}
 9
10   #[Route('products/all', priority: 10)]
11   public function all(){}
12
13}

La valeur par défaut pour priority est 0.

Mise en cache de réponse d’une route

Il est possible de mettre en cache la réponse produite par une route :

Dans ce cas, la réponse est en cache et n’est plus générée dynamiquement.

#[Route('products/all', cache: true)]
public function all(){}

Durée du cache

La duration est exprimée en secondes, si elle est omise, la validité du cache est infinie.

#[Route('products/all', cache: true, duration: 3600)]
public function all(){}

Expiration du cache

Il est possible de forcer le rechargement de la réponse en supprimant le cache associé.

Router::setExpired("products/all");

Mise en cache des routes dynamiques

Les routes dynamiques peuvent également être mises en cache.

Important

Cette possibilité n’a d’intérêt que si cette mise en cache n’est pas faite en production, mais au moment de l’initialisation du cache.

Router::get("foo", function(){
   echo 'Hello world!';
});

Router::addRoute("string", \controllers\Main::class,"index");
CacheManager::storeDynamicRoutes(false);

Vérification des routes avec les devtools :

Ubiquity info:routes
../_images/info-routes.png

Gestion des erreurs (404 & 500)

Routage par défaut

Avec le système de routage par défaut (le couple contrôleur+action définissant une route), un gestionnaire d’erreurs peut être redéfini pour personnaliser la gestion des erreurs.

Dans le fichier de configuration app/config/config.php, ajoutez la clé onError, associée à un callback définissant les messages d’erreur :

"onError"=>function ($code, $message = null,$controller=null){
   switch($code){
      case 404:
         $init=($controller==null);
         \Ubiquity\controllers\Startup::forward('IndexController/p404',$init,$init);
         break;
   }
}

Implémente l’action sollicitée p404 dans IndexController :

app/controllers/IndexController.php
...

public function p404(){
   echo "<div class='ui error message'><div class='header'>404</div>The page you are looking for doesn't exist!</div>";
}

Routage avec annotations

Il suffit dans ce cas d’ajouter une dernière route désactivant le système de routage par défaut, et correspondant à la gestion de l’erreur 404 :

app/controllers/IndexController.php
...

#[Route('{url}', priority: -1000)]
public function p404($url){
   echo "<div class='ui error message'><div class='header'>404</div>The page `$url` you are looking for doesn't exist!</div>";
}