El RoutingExtraBundle integra el enrutado dinámico a Symfony utilizando Enrutando.
La ChainRouter pretende reemplazar el enrutador predefinido de Symfony. Todo lo que hace es recoger una lista priorizada de rutas e intenta emparejar peticiones y generar las URL con todas ellas. Naturalmente, una de las rutas en esa cadena puede ser la ruta predefinida para que todavía la puedas utilizar de la manera estándar en algunas de tus rutas.
Además, este paquete entrega útiles implementaciones de enrutado. Actualmente, existe el DynamicRouter que enruta basándose en una lógica de cargador personalizada para los objetos Ruta de Symfony2. Puedes implementar el proveedor usando una base de datos, por ejemplo con el PHPCR-ODM de Doctrine o el ORM de Doctrine. Este paquete proporciona una implementación predefinida para el PHPCR-ODM de Doctrine.
El servicio DynamicRouter sólo está disponible cuándo se habilita explícitamente en la configuración de la aplicación.
Finalmente estos paquetes proporcionan documentos de ruta para el PHPCR-ODM de Doctrine y un controlador para redirigir rutas.
La ChainRouter puede reemplazar el sistema de enrutado predefinido de Symfony con una implementación habilitada en cadena. Esta no enruta nada por sí misma, sino que únicamente recorre todos los enrutadores encadenados. Para manejar las rutas estándar configuradas en Symfony, el enrutador predefinido de Symfony se puede poner en la cadena.
En tu archivo app/config/config.yml, puedes especificar cual servicio de enrutado quieres usar. Si no especificas el routers_by_id asigna todos, de manera predefinida el enrutador encadenado simplemente cargará el enrutador construido en Symfony. Cuándo especificas la lista de routers_by_id, necesitas tener una entrada para router.default si quieres el enrutador de Symfony2 (aquel que lee las rutas desde app/config/routing.yml).
El formato es service_name: priority — el número de prioridad más alto anterior es preguntado a este servicio enrutador para emparejar una ruta o para generar una url:
# app/config/config.yml
symfony_cmf_routing_extra:
chain:
routers_by_id:
# activa el DynamicRouter con alta prioridad para redefinir
# el enrutador configurado con contenido
symfony_cmf_routing_extra.dynamic_router: 200
# activa el enrutador predefinido de symfony con
# baja prioridad
router.default: 100
# cuando la cadena de enrutadores debe sustituir al enrutador predefinido. por omisión es true
# si lo pones en false, el enrutador sólo estará disponible como servicio
# symfony_cmf_routing_extra.router y debes hacer algo para lanzarlo
# replace_symfony_router: true
Tus enrutadores se pueden registrar automáticamente, justo añadiéndolos como servicios etiquetados con router y una prioridad opcional. Mientras más alta prioridad, más temprano será consultado tu enrutador para emparejar la ruta. Si no especificas la prioridad, tu enrutador vendrá al último. Si hay varios enrutadores con la misma prioridad, el orden entre ellos es indeterminado. El servicio etiquetado tendrá esta apariencia:
services:
my_namespace.my_router:
class: %my_namespace.my_router_class%
tags:
- { name: router, priority: 300 }
<service id="my_namespace.my_router" class="%my_namespace.my_router_class%">
<tag name="router" priority="300" />
..
</service>
Ve también la documentación oficial de Symfony2 para las etiquetas de inyección de dependencias
Esta implementación de un enrutador utiliza NestedMatcher el cuál carga rutas de una RouteProviderInterface. La interfaz del proveedor se puede implementar fácilmente con Doctrine.
El enrutador trabaja con clases extendidas de UrlMatcher y UrlGenerator que añaden la carga de rutas desde la base de datos y el concepto de contenido referido.
El servicio NestedMatcher está configurado con un proveedor de rutas. Ve cómo cambiar el route_repository_service en la sección de configuración y la siguiente sección para más detalles de la implementación predeterminada basada en PHPCR-ODM.
Quzás quieras configurar potenciadores de enrutado para decidir cual controlador usar para manejar la petición, para evitar la codificación directa de los nombres de controlador para enrutar tus documentos.
La configuración mínima requerida para cargar el enrutador dinámico como un servicio symfony_cmf_routing_extra.dynamic_router es tenerlo como enabled: true en tu archivo config.yml (El enrutador se activa automáticamente apenas añadas cualquier otra configuración a la entrada dynamic). Sin habilitarlo, el servicio enrutador dinámico no será cargado en absoluto, dejándote utilizar la ChainRouter con tus propios enrutadores:
# app/config/config.yml
symfony_cmf_routing_extra:
dynamic:
enabled: true
Este paquete viene con una implementación de enrutador con repositorio para PHPCR-ODM. PHPCR es apropiado para la naturaleza del árbol de datos. Si usas PHPCR-ODM con un documento de ruta como el proporcionado, justo puedes dejar el servicio repositorio como el predefinido.
El repositorio predefinido carga la ruta desde la ruta en la petición y todas las rutas padre para permitir que algunos de los segmentos de la ruta sigan siendo parámetros. Si necesitas una diferente manera de cargar rutas o, por ejemplo, nunca usar parámetros, puedes escribir tu propia implementación del repositorio para optimizarla (ve en el archivo cmf_routing.xml cómo configurar el servicio).
La mayoría del proceso de concordancia está descrito en la documentación del componente enrutador del CMF. La única diferencia es que el paquete colocará el contentDocument en los atributos de la petición en vez de en la ruta predefinida.
Tus controladores pueden (y tienen que) declarar el parámetro $contentDocument en tus métodos Action si se supone que trabajen con el contenido referido por las rutas. Ve Symfony\Cmf\Bundle\ContentBundle\Controller\ContentController para un ejemplo.
Para configurar qué controlador se utiliza para cuál contenido, puedes especificar potenciadores de ruta. La presencia de cualquiera de los potenciadores en la configuración hace que el contenedor de dependencias inyecte al DynamicRouter el potenciador respectivo.
Los posibles potenciadores son (en orden de precedencia):
(Controlador explícito): Si existe un _controller en getRouteDefaults(), ningún potenciador lo sustituye.
Plantilla explícita: Requiere que el documento de ruta regrese un parámetro '_template' en getRouteDefaults. El controlador genérico configurado está puesto por el potenciador.
Controlador por alias: Requiere que el documento de ruta regrese un valor 'type' en getRouteDefaults()
class names in the map and if matched that controller is used. Se utiliza instanceof en vez de compararlo directamente para trabajar con clases delegadas y extender otras clases.
class names in the map and if matched that template will be set as ‘_template’ in the $defaults and the generic controller used as controller.
# app/config/config.yml
symfony_cmf_routing_extra:
dynamic:
generic_controller: symfony_cmf_content.controller:indexAction
controllers_by_type:
editablestatic: sandbox_main.controller:indexAction
controllers_by_class:
Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent: symfony_cmf_content.controller:indexAction
templates_by_class:
Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent: SymfonyCmfContentBundle:StaticContent:index.html.twig
# el proveedor de ruta es responsable de cargar rutas.
manager_registry: doctrine_phpcr
manager_name: default
# Si usas la ruta predefinida del servicio repositorio de
# Doctrine, puedes usar esto para personalizar la ruta raíz
# para el RouteProvider de PHPCR-ODM. Esta ruta base
# es inyectada por el escucha\IdPrefix - pero sólo para
# las rutas que coincidan con el prefijo, para permitir
# más de una ruta fuente.
routing_repositoryroot: /cms/routes
# Si deseas reemplazar el proveedor de ruta predefinido
# o tu repositorio de contenido puedes especificar sus
# identificadores de servicio aquí.
route_provider_service_id: my_bundle.provider.endpoint
content_repository_service_id: my_bundle.repository.endpoint
# Un proveedor orm podría necesitar diferente configuración.
# Ve un ejemplo en cmf_routing.xml si necesitas definir
# tu propio servicio
Para ver algunos ejemplos, por favor ve en el recinto de seguridad del CMF, específicamente la carga de accesorios de enrutado.
Truco
You can also define your own RouteEnhancer classes for specific use cases. See Personalizando.
Todas las clases de ruta deben extender la clase Route del núcleo de Symfony. Los documentos tampoco se pueden crear por código (por ejemplo un guión de accesorios) o con una interfaz web como la proporcionada para la administración PHPCR-ODM de Sonata (ve abajo).
PHPCR-ODM asocia todas las características de la ruta del núcleo al almacenamiento, así que puedes utilizar normalmente los métodos setDefault, setRequirement, setOption y setHostnamePattern. Además cuándo creas una ruta, puedes definir si .{_format} se debería anexar al patrón y configurar con requisitos el _format requerido. La otra opción del constructor te permite controlar si la ruta debería anexar una barra inclinada final porque esto no se puede expresar con un nombre PHPCR. El predefinido es que no tenga ninguna barra inclinada final.
Todas las rutas están localizadas bajo una ruta configurada como la raíz, por ejemplo '/cms/rutas'. Puedes crear una nueva ruta en código PHP de la siguiente manera:
use Symfony\Cmf\Bundle\RoutingExtraBundle\Document\Route;
$route = new Route;
$route->setParent($dm->find(null, '/routes'));
$route->setName('projects');
// establece el controlador explícitamente (ambas sintaxis trabajan
// servicio y Paquete:Nombre:acción)
$route->setDefault('_controller', 'sandbox_main.controller:specialAction');
Sin embargo, el ejemplo anterior probablemente se debería haber hecho como una ruta configurada en un archivo xml/yml de Symfony, a no ser que se suponga que el usuario final cambie la URL o el controlador.
Para enlazar un contenido a esta ruta, sencillamente pon lo siguiente en el documento.
$content = new Content('my content'); // el contenido se debe asociar a una clase
$route->setRouteContent($content);
Esto pondrá el documento en los parámetros de la petición y si tu controlador especifica un parámetro llamado $contentDocument, le será pasado este documento.
También puedes utilizar patrones variables para la URL y definir requisitos y predeterminados.
// No olvides la barra inclinada inicial si quieres /proyectos/{id} y no /proyectos{id}
$route->setVariablePattern('/{id}');
$route->setRequirement('id', '\d+');
$route->setDefault('id', 1);
Esto te dará una ruta que empareja con la URL /proyectos/<número> pero también con /proyectos ya que es el predefinido para el parámetro id. Esta emparejará con /proyectos/7 así como con /proyectos pero no con /proyectos/x-4. El documento todavía será almacenado en /rutas/proyectos. Esto trabajará porque, como se mencionó anteriormente, el proveedor de ruta buscará documentos de ruta en todas las rutas posibles y elegirá la primera que coincida. En este ejemplo, si hay un documento de ruta en /rutas/proyectos/7 que coincida (sin más parámetros) será seleccionado. De lo contrario comprobará si /rutas/proyectos tiene un patrón que coincida. Si no, el documento superior en /rutas será comprobado.
Naturalmente también puedes tener varios parámetros, como es normal en las rutas de Symfony. La semántica y reglas para patrones, predefinidos y requisitos son exactamente iguales que en las rutas del núcleo.
Tu controlador puede esperar el parámetro $id así como el $contentDocument tal como se puso un contenido en la ruta. El contenido se podría usar para definir una sección de introducción que sea igual en cada proyecto u otro dato compartido. Si no necesitas contenido, puedes simplemente no ponerlo en el documento.
Si añades sonata-project/doctrine-phpcr-admin-bundle en la sección require de tu archivo composer.json y el SonataDoctrinePhpcrAdminBundle es cargado en el núcleo de la aplicación, los documentos de ruta son expuestos en el SonataDoctrinePhpcrAdminBundle. Para instrucciones sobre cómo configurar este paquete ve SonataDoctrinePhpcrAdminBundle.
De manera predefinida, use_sonata_admin se configura automáticamente basándose en si SonataDoctrinePhpcrAdminBundle está disponible, pero lo puedes deshabilitar explícitamente no poniéndolo incluso si sonata está habilitado, o lo puedes habilitar explícitamente para obtener un error si sonata queda inutilizable.
Si quieres utilizar el admin, debes configurar la opción content_basepath para que apunte a la raíz de tus documentos de contenido.
# app/config/config.yml
symfony_cmf_routing_extra:
use_sonata_admin: auto # usa true/false para forzar el uso / no uso de
# la administración de sonata
content_basepath: ~ # usado con la administración de sonata para
# manejar el contenido, por omisión a
# symfony_cmf_core.content_basepath
El paquete define un tipo form que puedes utilizar para la clásica casilla de verificación «aceptar términos» donde colocas las url en la etiqueta. Simplemente especifica symfony_cmf_routing_extra_terms_form_type como el nombre del tipo form y especifica una etiqueta y un arreglo con content_ids en las opciones
$form->add('terms',
'symfony_cmf_routing_extra_terms_form_type',
array('label' => 'I have seen the <a href="%team%">Team</a> and <a href="%more%">More</a> pages ...',
'content_ids' => array('%team%' => '/cms/content/static/team',
'%more%' => '/cms/content/static/more')
));
El tipo form automáticamente genera las rutas para el contenido especificado y pasa las rutas al ayudante trans de Twig para sustituirlas en la etiqueta.
Ve la documentación del componente enrutador del CMF para información sobre la RouteObjectInterface, redirecciones y regiones.
Notas:
# app/config/config.yml
symfony_cmf_routing_extra:
controllers_by_class:
Symfony\Cmf\Component\Routing\RedirectRouteInterface: symfony_cmf_routing_extra.redirect_controller:redirectAction
Puedes añadir más implementaciones de la RouteEnhancerInterface si tienes un caso no manejado por el proporcionado. Sencillamente define servicios para tus potenciadores y eiquétalos con dynamic_router_route_enhancer para añadirlos al enrutado.
Si usas un ODM/ORM diferente a PHPCR-ODM, probablemente necesites especificar la clase para la entidad ruta (en PHPCR-ODM, la clase es detectada automáticamente). Para necesidades más específicas, dale una mirada al DynamicRouter y ve si lo quieres extender. También puedes escribir tus propios enrutadores para engancharlos a la cadena.