Enrutador (Router)
El enrutamiento se puede utilizar además del mecanismo por defecto que asocia controller/action/{parameters}
con una url.
Rutas dinámicas
Las rutas dinámicas se definen en tiempo de ejecución.
Es posible definir estas rutas en el archivo app/config/services.php.
Importante
Las rutas dinámicas sólo deben utilizarse si la situación lo requiere:
en el caso de una microaplicación
si una ruta debe definirse dinámicamente
En todos los demás casos, es aconsejable declarar las rutas con anotaciones, para beneficiarse del almacenamiento en caché.
Rutas de devolución de llamadas (callback)
Las rutas más básicas de Ubiquity aceptan un Closure.
En el contexto de las microaplicaciones, este método evita tener que crear un controlador.
1 use Ubiquity\controllers\Router;
2
3 Router::get("foo", function(){
4 echo 'Hello world!';
5 });
Se pueden definir rutas de devolución de llamada para todos los métodos http con:
Router::post
Router::put
Router::delete
Router::patch
Router::options
Rutas de controlador
Las rutas también pueden asociarse de forma más convencional a una acción de un controlador:
1 use Ubiquity\controllers\Router;
2
3 Router::addRoute('bar', \controllers\FooController::class,'index');
El método FooController::index()
será accesible a través de la url /bar
.
En este caso, el FooController debe ser una clase que herede de Ubiquitycontrollers\Controller o una de sus subclases, y debe tener un método index:
1 namespace controllers;
2
3 class FooController extends ControllerBase{
4
5 public function index(){
6 echo 'Hello from foo';
7 }
8 }
Ruta por defecto
La ruta por defecto coincide con la ruta /.
Puede definirse utilizando la ruta reservada _default.
1 use Ubiquity\controllers\Router;
2
3 Router::addRoute("_default", \controllers\FooController::class,'bar');
Rutas estáticas
Las rutas estáticas se definen mediante anotaciones o con atributos nativos de php desde Ubiquity 2.4.0
.
Nota
Estas anotaciones o atributos nunca se leen en tiempo de ejecución.
Es necesario reiniciar la caché del enrutador para tener en cuenta los cambios realizados en las rutas.
Creación
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}
El método Products::index()
será accesible a través de la url /products
.
Nota
- La barra inicial o terminal se ignora en la ruta. Por lo tanto, las siguientes rutas son equivalentes:
#[Route('products')]
#[Route('/products')]
#[Route('/products/')]
Parámetros de ruta
Una ruta puede tener parámetros:
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}
Parámetros opcionales de ruta
Una ruta puede definir parámetros opcionales, si el método asociado tiene argumentos opcionales:
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}
Requisitos de ruta
Es posible añadir especificaciones sobre las variables pasadas en la url mediante el atributo 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 ruta definida coincide con estas urls:
products/all/1/20
products/all/5/
- pero no con ese:
products/all/test
Tipificación de parámetros
La declaración de ruta tiene en cuenta los tipos de datos pasados a la acción, lo que evita añadir requisitos para tipos 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 ruta definida coincide con estas urls:
products/1
products/20
- pero no con ese:
products/test
- Valores correctos por tipo de datos:
int
:1
…bool
:0
or1
float
:1
1.0
…
Métodos http de enrutado
Es posible especificar el método o métodos http asociados a una ruta:
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}
El atributo methods puede aceptar varios métodos:
@route("testMethods", "methods"=>["get", "post", "delete"])
#[Route('testMethods', methods: ['get','post','delete'])]
La anotación @route o atributo Route se aplica por defecto a todos los métodos HTTP.
Existe una anotación específica para cada uno de los métodos HTTP existentes:
@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}
Nombre de ruta
Es posible especificar el name de una ruta, este nombre facilita entonces el acceso a la url asociada.
Si no se especifica el atributo name, cada ruta tiene un nombre por defecto, basado en el patrón 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}
Generación de URL o rutas
Los nombres de ruta pueden utilizarse para generar URL o rutas.
Enlaces a páginas en Twig
<a href="{{ path('products.index') }}">Products</a>
Ruta global
La anotación @route puede utilizarse en una clase de controlador :
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}
En este caso, la ruta definida en el controlador se utiliza como prefijo para todas las rutas del controlador :
La ruta generada para la acción display es /product/all
.
rutas automatizadas
Si se define una ruta global, es posible añadir todas las acciones del controlador como rutas (utilizando el prefijo global), estableciendo el parámetro 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}
- El atributo automated define las 3 rutas contenidas en ProductsController:
/product/(index/)?
/product/generate
/product/display/{id}
rutas heredadas
Con el atributo inherited, también es posible generar las rutas declaradas en las clases base, o generar rutas asociadas a acciones de clases base si el atributo automated se establece a true al mismo tiempo.
La clase 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 clase derivada que utiliza miembros heredados:
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}
- El atributo inherited define las 2 rutas definidas en ProductsBase:
/products/(index/)?
/products/sort/{name}
Si se combinan los atributos automated y inherited, las acciones de la clase base también se añaden a las rutas.
Parámetros de ruta globales
La parte global de una ruta puede definir parámetros, que serán pasados en todas las rutas generadas.
Estos parámetros se pueden recuperar a través de un miembro de datos público:
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}
Accediendo a la url /foo/bar/display
se muestra el contenido del miembro bar.
Ruta sin prefijo global
Si la ruta global se define en un controlador, todas las rutas generadas en este controlador irán precedidas del prefijo.
Es posible introducir explícitamente excepciones en algunas rutas, utilizando el prefijo #/
.
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}
El controlador define la url /noRoot
, que no está prefijada con la parte /foo
.
Prioridad de ruta
El parámetro prority de una ruta permite resolver esta ruta en un orden de prioridad.
Cuanto mayor sea el parámetro de prioridad, más se definirá la ruta al principio de la pila de rutas en la caché.
En el ejemplo siguiente, la ruta products/all se definirá antes que la ruta products.
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}
El valor de prioridad por defecto es 0
.
Caché de respuesta de rutas
Es posible almacenar en caché la respuesta producida por una ruta:
En este caso, la respuesta se almacena en caché y deja de ser dinámica.
#[Route('products/all', cache: true)]
public function all(){}
/**
* @route("products/all","cache"=>true)
*/
public function all(){}
Duración de la caché
La duración se expresa en segundos, si se omite, la duración de la caché es infinita.
#[Route('products/all', cache: true, duration: 3600)]
public function all(){}
/**
* @route("products/all","cache"=>true,"duration"=>3600)
*/
public function all(){}
Expiración de la caché
Es posible forzar la recarga de la respuesta borrando la caché asociada.
Router::setExpired("products/all");
Cacheo dinámico de rutas
Las rutas dinámicas también pueden almacenarse en caché.
Importante
Esta posibilidad sólo es útil si esta caché no se realiza en producción, sino en el momento de inicializar la caché.
Router::get("foo", function(){
echo 'Hello world!';
});
Router::addRoute("string", \controllers\Main::class,"index");
CacheManager::storeDynamicRoutes(false);
Comprobación de rutas con devtools :
Ubiquity info:routes
Gestión de errores (errores 404 y 500)
Sistema de enrutamiento por defecto
Con el sistema de enrutamiento por defecto (la pareja controlador+acción definiendo una ruta), el manejador de errores puede ser redefinido para personalizar la gestión de errores.
En el fichero de configuración app/config/config.php, añade la clave onError, asociada a un devolucion de llamada (callback) que defina los mensajes de error:
"onError"=>function ($code, $message = null,$controller=null){
switch($code){
case 404:
$init=($controller==null);
\Ubiquity\controllers\Startup::forward('IndexController/p404',$init,$init);
break;
}
}
Implementar la acción solicitada p404 en el 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>";
}
Enrutado con anotaciones
Basta en este caso con añadir una última ruta deshabilitando el sistema de enrutamiento por defecto, y correspondiente a la gestión del error 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>";
}