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.
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 :
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 :
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
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
1namespace controllers;
2
3use Ubiquity\attributes\items\router\Route;
4
5class ProductsController extends ControllerBase{
6
7 #[Route('products')]
8 public function index(){}
9
10}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4
5 /**
6 * @route("products")
7 */
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 :
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4...
5 /**
6 * @route("products/{value}")
7 */
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 :
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4 ...
5 /**
6 * @route("products/all/{pageNum}/{countPerPage}")
7 */
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.
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4 ...
5 /**
6 * @route("products/all/{pageNum}/{countPerPage}","requirements"=>["pageNum"=>"\d+","countPerPage"=>"\d?"])
7 */
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).
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4 ...
5 /**
6 * @route("products/{productNumber}")
7 */
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
or1
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 :
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4
5 /**
6 * @route("products","methods"=>["get","post"])
7 */
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
1namespace controllers;
2
3use Ubiquity\attributes\items\router\Get;
4
5class ProductsController extends ControllerBase{
6
7 #[Get('products')]
8 public function index(){}
9
10}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4
5 /**
6 * @get("products")
7 */
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.
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4
5 /**
6 * @route("products","name"=>"products.index")
7 */
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 :
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}
1namespace controllers;
2/**
3 * @route("/product")
4 */
5class ProductsController extends ControllerBase{
6
7 ...
8 /**
9 * @route("/all")
10 */
11 public function display(){}
12
13}
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 :
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}
1namespace controllers;
2/**
3 * @route("/product","automated"=>true)
4 */
5class ProductsController extends ControllerBase{
6
7 public function index(){}
8
9 public function generate(){}
10
11 public function display($id){}
12
13}
- 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 :
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 }
1namespace controllers;
2
3abstract class ProductsBase extends ControllerBase{
4
5 /**
6 *@route("(index/)?")
7 */
8 public function index(){}
9
10 /**
11 * @route("sort/{name}")
12 */
13 public function sortBy($name){}
14
15}
La classe dérivée utilisant les membres hérités :
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}
1namespace controllers;
2/**
3 * @route("/product","inherited"=>true)
4 */
5class ProductsController extends ProductsBase{
6
7 public function display(){}
8
9}
- 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 :
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}
1namespace controllers;
2
3/**
4 * @route("/foo/{bar}","automated"=>true)
5 */
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 #/
.
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}
1namespace controllers;
2
3/**
4 * @route("/foo","automated"=>true)
5 */
6class FooController {
7
8 /**
9 * @route("#/noRoot")
10 */
11 public function noRoot(){}
12
13}
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.
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}
1namespace controllers;
2
3class ProductsController extends ControllerBase{
4
5 /**
6 * @route("products","priority"=>1)
7 */
8 public function index(){}
9
10 /**
11 * @route("products/all","priority"=>10)
12 */
13 public function all(){}
14
15}
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(){}
/**
* @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(){}
/**
* @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
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 :
...
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 :
...
#[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>";
}
...
/**
* @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>";
}