El componente Routing asocia una Petición HTTP a un conjunto de variables de configuración.
Puedes instalar el componente de varias maneras diferentes:
Con el fin de establecer un sistema de enrutado básico necesitas tres partes:
Veamos un ejemplo rápido. Ten en cuenta que esto supone que ya has configurado el cargador automático para cargar el componente Routing:
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$route = new Route('/foo', array('controller' => 'MyController'));
$routes = new RouteCollection();
$routes->add('route_name', $route);
$context = new RequestContext($_SERVER['REQUEST_URI']);
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->match('/foo');
// array('controller' => 'MyController', '_route' => 'route_name')
Nota
Ten cuidado al usar $_SERVER['REQUEST_URI'], ya que este puede incluir los parámetros de consulta en la URL, lo cual causará problemas con la ruta coincidente. Una manera fácil de solucionar esto es usando el componente HTTPFoundation como se explica abajo.
Puedes agregar tantas rutas como quieras a una clase Symfony\Component\Routing\RouteCollection.
El método RouteCollection::add() toma dos argumentos. El primero es el nombre de la ruta. El segundo es un objeto Symfony\Component\Routing\Route, que espera una ruta URL y algún arreglo de variables personalizadas en su constructor. Este arreglo de variables personalizadas puede ser cualquier cosa que tenga significado para tu aplicación, y es devuelto cuando dicha ruta corresponda.
Si no hay ruta coincidente debe lanzar una Symfony\Component\Routing\Exception\ResourceNotFoundException.
Además de tu arreglo de variables personalizadas, se añade una clave _route, que contiene el nombre de la ruta buscada.
Una definición de ruta completa puede contener hasta siete partes:
Nuevo en la versión 2.2: El soporte necesario para emparejar con el servidor se añadió en Symfony 2.2
Veamos la siguiente ruta, que combina varias de estas ideas:
$route = new Route(
'/archive/{month}', // ruta
array('controller' => 'showArchive'), // valores predefinidos
array('month' => '[0-9]{4}-[0-9]{2}', 'subdomain' => 'www|m'), // requisitos
array(), // opciones
'{subdomain}.example.com', // servidor
array(), // esquemas
array() // métodos
);
// ...
$parameters = $matcher->match('/archive/2012-01');
// array(
// 'controller' => 'showArchive',
// 'month' => '2012-01',
// 'subdomain' => 'www',
// '_route' => ...
// )
$parameters = $matcher->match('/archive/foo');
// lanza la ResourceNotFoundException
En este caso, la ruta coincide con /archive/2012-01, porque el comodín {month} coincide con el comodín de la expresión regular suministrada. Sin embargo, /archive/f oo no coincide, porque el comodín del mes «foo» no concuerda.
Truco
Si quieres hacer coincidir todas las URL que comiencen con una cierta ruta y terminan en un sufijo arbitrario puedes utilizar la siguiente definición de ruta:
$route = new Route(
'/start/{suffix}',
array('suffix' => ''),
array('suffix' => '.*')
);
Puedes agregar rutas u otras instancias de Symfony\Component\Routing\RouteCollection a otra colección. De esta manera puedes construir un árbol de rutas. Además, puedes definir un prefijo, requisitos predeterminados, opciones predefinidas y servidores a todas las rutas de un subárbol con el método addPrefix():
$rootCollection = new RouteCollection();
$subCollection = new RouteCollection();
$subCollection->add(...);
$subCollection->add(...);
$subCollection->addPrefix(
'/prefix', // prefijo
array(), // requisitos
array(), // opciones
'admin.example.com', // servidor
array('https') // esquemas
);
$rootCollection->addCollection($subCollection);
Nuevo en la versión 2.2: El método addPrefix se añadió en Symfony 2.2. Este era parte del método addCollection en versiones anteriores.
La clase Symfony\Component\Routing\RequestContext proporciona información sobre la Petición actual. Puedes definir todos los parámetros de una Petición HTTP con esta clase a través de su constructor:
public function __construct(
$baseUrl = '',
$method = 'GET',
$host = 'localhost',
$scheme = 'http',
$httpPort = 80,
$httpsPort = 443
)
Normalmente puedes pasar los valores de la variable $_SERVER para poblar la Symfony\Component\Routing\RequestContext. Pero si utilizas el componente HttpFoundation, puedes utilizar su clase Symfony\Component\HttpFoundation\Request para alimentar al Symfony\Component\Routing\RequestContext en un método abreviado:
use Symfony\Component\HttpFoundation\Request;
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());
Si bien la Symfony\Component\Routing\Matcher\UrlMatcher trata de encontrar una ruta que se adapte a la petición dada, esta también puede construir una URL a partir de una ruta determinada:
use Symfony\Component\Routing\Generator\UrlGenerator;
$routes = new RouteCollection();
$routes->add('show_post', new Route('/show/{slug}'));
$context = new RequestContext($_SERVER['REQUEST_URI']);
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate('show_post', array(
'slug' => 'my-blog-post',
));
// /show/my-blog-post
Nota
Si definiste un esquema, se genera una URL absoluta si el esquema de la clase Symfony\Component\Routing\RequestContext actual no concuerda con el requisito.
Ya hemos visto lo fácil que es añadir rutas a una colección dentro de PHP. Pero también puedes cargar rutas de una serie de archivos diferentes.
El componente de enrutado viene con una serie de clases cargadoras, cada una dotándote de la capacidad para cargar una colección de definiciones de ruta desde un archivo externo en algún formato. Cada cargador espera una instancia del Symfony\Component\Config\FileLocator como argumento del constructor. Puedes utilizar el Symfony\Component\Config\FileLocator para definir una serie de rutas en las que el cargador va a buscar los archivos solicitados. Si se encuentra el archivo, el cargador devuelve una Symfony\Component\Routing\RouteCollection.
Si estás usando el YamlFileLoader, entonces las definiciones de ruta tienen este aspecto:
# routes.yml
route1:
path: /foo
defaults: { _controller: 'MyController::fooAction' }
route2:
path: /foo/bar
defaults: { _controller: 'MyController::foobarAction' }
Para cargar este archivo, puedes utilizar el siguiente código. Este asume que tu archivo routes.yml está en el mismo directorio que el código de abajo:
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
// busca dentro de *este* directorio
$locator = new FileLocator(array(__DIR__));
$loader = new YamlFileLoader($locator);
$collection = $loader->load('routes.yml');
Además del Symfony\Component\Routing\Loader\YamlFileLoader hay otros dos cargadores que funcionan de manera similar:
Si utilizas el Symfony\Component\Routing\Loader\PhpFileLoader debes proporcionar el nombre del un archivo php que devuelva una Symfony\Component\Routing\RouteCollection:
// ProveedorDeRuta.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(
'route_name',
new Route('/foo', array('controller' => 'ExampleController'))
);
// ...
return $collection;
También está el Symfony\Component\Routing\Loader\ClosureLoader, que llama a un cierre y utiliza el resultado como una Symfony\Component\Routing\RouteCollection:
use Symfony\Component\Routing\Loader\ClosureLoader;
$closure = function() {
return new RouteCollection();
};
$loader = new ClosureLoader();
$collection = $loader->load($closure);
Por último, pero no menos importante, están las Symfony\Component\Routing\Loader\AnnotationDirectoryLoader y Symfony\Component\Routing\Loader\AnnotationFileLoader para cargar definiciones de ruta a partir de las anotaciones de la clase. Los detalles específicos se dejan aquí.
La clase Symfony\Component\Routing\Router es un paquete todo en uno para utilizar rápidamente el componente de enrutado. El constructor espera una instancia del cargador, una ruta a la definición de la ruta principal y algunas otras opciones:
public function __construct(
LoaderInterface $loader,
$resource,
array $options = array(),
RequestContext $context = null,
array $defaults = array()
);
Con la opción cache_dir puedes habilitar la caché de enrutado (si proporcionas una ruta) o desactivar el almacenamiento en caché (si la configuras a null). El almacenamiento en caché automáticamente se hace en segundo plano si lo quieres usar. Un ejemplo básico de la clase Symfony\Component\Routing\Router se vería así:
$locator = new FileLocator(array(__DIR__));
$requestContext = new RequestContext($_SERVER['REQUEST_URI']);
$router = new Router(
new YamlFileLoader($locator),
'routes.yml',
array('cache_dir' => __DIR__.'/cache'),
$requestContext
);
$router->match('/foo/bar');
Nota
Si utilizas el almacenamiento en caché, el componente Routing compilará las nuevas clases guardándolas en cache_dir. Esto significa que el archivo debe tener permisos de escritura en esa ubicación.