Esta es una introducción para entender los conceptos detrás del enrutado del CMF. Para la documentación de referencia por favor ve Enrutando y RoutingExtraBundle.
Los CMS son sitios altamente dinámicos, donde la mayoría del contenido lo gestionan los administradores en lugar de los desarrolladores. El número de páginas disponibles fácilmente puede alcanzar miles, lo cual normalmente es multiplicado por el número de traducciones disponibles. Mejor accesibilidad y prácticas SEO, así como preferencias de usuario dictan que direcciones URL deberían ser definibles por los gestores de contenido.
El mecanismo de enrutado predefinido de Symfony2, con enfoque de archivos de configuración, no es la mejor solución para este problema, puesto que no es apropiado para manejar rutas dinámicas definidas por el usuario, ni escalan bien para un gran número de rutas.
Para afrontar estos problemas, se desarrolló un nuevo sistema de enrutado, este tiene en cuenta las necesidades típicas de enrutado de un CMS:
Con estos requisitos en mente, fue desarrollado el componente Routing del CMF de Symfony.
En el núcleo del CMF de Symfony el componente de enrutado es la ChainRouter. Esta se utiliza como sustituta para el sistema de enrutado predefinido de Symfony2 y, como tal, es responsable de determinar cuál Controlador manejará cada petición.
The ChainRouter works by accepting a set of prioritized routing strategies, Symfony\Component\Routing\RouterInterface implementations, commonly referred to as “Routers”. The routers are responsible for matching an incoming request to an actual Controller and, to do so, the ChainRouter iterates over the configured Routers according to their configured priority:
# app/config/config.yml
symfony_cmf_routing_extra:
chain:
routers_by_id:
# enable the DynamicRouter with high priority to allow overwriting
# configured routes with content
symfony_cmf_routing_extra.dynamic_router: 200
# activa el enrutador predefinido de symfony con
# baja prioridad
router.default: 100
<!-- app/config/config.xml -->
<symfony-cmf-routing-extra:config>
<symfony-cmf-routing-extra:chain>
<symfony-cmf-routing-extra:routers-by-id
id="symfony-cmf-routing-extra.dynamic-router">
200
</symfony-cmf-routing-extra:routers-by-id>
<symfony-cmf-routing-extra:routers-by-id
id="router.default">
100
</symfony-cmf-routing-extra:routers-by-id>
</symfony-cmf-routing-extra:chain>
</symfony-cmf-routing-extra:config>
// app/config/config.php
$container->loadFromExtension('symfony_cmf_routing_extra', array(
'chain' => array(
'routers_by_id' => array(
'symfony_cmf_routing_extra.dynamic_router' => 200,
'router.default' => 100,
),
),
));
You can also load Routers using tagged services, by using the router tag and an optional priority. 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 se verá como este:
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>
$container
->register('my_namespace.my_router', '%my_namespace.my_router_class%')
->addTag('router', array('priority' => 300))
;
El sistema de enrutado del CMF de Symfony añade un nuevo DynamicRouter, el cual complementa el Router predefinido de Symfony2.
A pesar de que reemplaza el mecanismo de enrutado predefinido, el Routing del CMF de Symfony te permite seguir utilizando el sistema existente. De hecho, el enrutado predefinido está habilitado por omisión, así que puedes seguir utilizando las rutas que declaraste en tus archivos de configuración, o como fueron declaradas por otros paquetes.
Este enrutador puede cargar instancias de Ruta dinámicamente desde un determinado proveedor. Luego usa un proceso para emparejar la petición entrante a una Ruta específica, la cual en turno suele determinar a qué Controlador enviar la petición.
La configuración predefinida del paquete declara que el DynamicRouter está inhabilitado por omisión. Para activarlo, sólo añade lo siguiente a tu archivo de configuración:
# app/config/config.yml
symfony_cmf_routing_extra:
dynamic:
enabled: true
<!-- app/config/config.xml -->
<symfony-cmf-routing-extra:config>
<symfony-cmf-routing-extra:dynamic enabled="true" />
</symfony-cmf-routing-extra:config>
// app/config/config.php
$container->loadFromExtension('symfony_cmf_routing_extra', array(
'dynamic' => array(
'enabled' => true,
),
));
Esta es la configuración mínima requirida para cargar el DynamicRouter como servicio, esta lo capacita para realizar cualquier enrutamiento. De hecho, cuándo exploras las páginas predefinidas que vienen con la EE del CMF de Symfony, el DynamicRouter es el que empareja tus peticiones con los Controladores y Plantillas.
Puedes configurar el proveedor a usar para adaptarlo a las necesidades de cada implementación, este debe implementar la RouteProviderInterface. Como parte de este paquete, se proporciona una implementación para el PHPCR-ODM, pero fácilmente puedes crear una propia, puesto que el Enrutador es de almacenamiento agnóstico. El proveedor predefinido carga la ruta como el camino en la petición y todas las rutas padre permiten que algunos segmentos de la ruta sean parámetros.
Para información más detallada sobre esta implementación y cómo la puedes personalizar o extender, consulta el RoutingExtraBundle.
El DynamicRouter es capaz de emparejar la petición entrante con un objeto Ruta del proveedor subyacente. Los detalles sobre cómo se lleva a cabo el proceso de emparejamiento se pueden encontrar en Enrutando.
Nota
Para hacer que el proveedor de rutas encuentre rutas, también necesitas proporcionar los datos en tu almacenamiento. Con PHPCR-ODM, esto se hace a través de la interfaz de admininistración (ve más adelante) o con accesorios.
No obstante, antes de poder explicar cómo hacerlo, necesitas entender cómo trabaja el DynamicRouter. An example will come later in this document.
Una Ruta necesita especificar cuál Controlador debería manejar una Petición específica. El DynamicRouter utiliza uno de varios métodos posibles para determinarlo (por orden de precedencia):
Apart from this, the DynamicRouter is also capable of dynamically specifying which Template will be used, in a similar way to the one used to determine the Controller (in order of precedence):
Aquí tienes un ejemplo sobre cómo configurar las opciones mencionadas anteriormente:
# 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
<!-- app/config/config.xml -->
<symfony-cmf-routing-extra:config>
<symfony-cmf-routing-extra:dynamic
generic-controller="symfony_cmf_content.controllerindexAction"
>
<symfony-cmf-routing-extra:controllers-by-type
type="editablestatic"
>
sandbox_main.controller:indexAction
</symfony-cmf-routing-extra:controllers-by-type>
<symfony-cmf-routing-extra:controllers-by-class
class="Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent"
>
symfony_cmf_content.controller::indexAction
</symfony-cmf-routing-extra:controllers-by-class>
<symfony-cmf-routing-extra:templates-by-class
alias="Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent"
>
SymfonyCmfContentBundle:StaticContent:index.html.twig
</symfony-cmf-routing-extra:templates-by-class>
</symfony-cmf-routing-extra:dynamic>
</symfony-cmf-routing-extra:config>
// app/config/config.php
$container->loadFromExtension('symfony_cmf_routing_extra', array(
'dynamic' => array(
'generic_controller' => 'symfony_cmf_content.controller:indexAction',
'controllers_by_type' => array(
'editablestatic' => 'sandbox_main.controller:indexAction',
),
'controllers_by_class' => array(
'Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent' => 'symfony_cmf_content.controller::indexAction',
),
'templates_by_class' => array(
'Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent' => 'SymfonyCmfContentBundle:StaticContent:index.html.twig',
),
),
));
Ten en cuenta que enabled: true ya no está presente. It’s only required if no other configuration parameter is provided. The router is automatically enabled as soon as you add any other configuration to the dynamic entry.
Nota
Internamente, el componente Routing asocia estas opciones de configuración a varias instancias de la RouteEnhancerInterface. El alcance real de estos potenciadores es mucho mayor, y puedes encontrar más información sobre ellos en la página Enrutando de la documentación.
Dependiendo de la lógica de tu aplicación, una URL solicitada puede tener una instancia del modelo asociado en la base de datos. Estas Rutas pueden implementar la RouteObjectInterface, y opcionalmente regresar una instancia del modelo, esta automáticamente será pasada al Controlador como la variable $contentDocument, si la declaras como parámetro.
Ten en cuenta que una Ruta puede implementar la interfaz mencionada anteriormente pero todavía no regresar ninguna instancia del modelo, en cuyo caso ningún objeto asociado será proporcionado.
Además, las Rutas que implementen esta interfaz también pueden tener un nombre de Ruta personalizado, en vez del predefinido compatible con el núcleo de Symfony, y pueden contener cualquier carácter. Esto te permite, por ejemplo, poner una ruta como el nombre de la ruta.
Puedes construir redirecciones implementando la RedirectRouteInterface. Si estás utilizando el proveedor de ruta PHPCR-ODM predefinido, uno listo para usar la implementación es proporcionado en el Documento RedirectRoute. Este puede redirigir o bien a una URI absoluta, a una Ruta nombrada que puede generar cualquier Enrutador en la cadena o a otro objeto Ruta conocido por el proveedor de rutas. La redirección real es manejada por un Controlador específico, el cual puedes configurar como gustes:
# app/config/config.yml
symfony_cmf_routing_extra:
controllers_by_class:
Symfony\Cmf\Component\Routing\RedirectRouteInterface: symfony_cmf_routing_extra.redirect_controller:redirectAction
<!-- app/config/config.xml -->
<symfony-cmf-routing-extra:config>
<symfony-cmf-routing-extra:controllers-by-class
class="Symfony\Cmf\Component\Routing\RedirectRouteInterface">
symfony_cmf_routing_extra.redirect_controller:redirectAction
</symfony-cmf-routing-extra:controllers-by-class>
</symfony-cmf-routing-extra:config>
// app/config/config.php
$container->loadFromExtension('symfony_cmf_routing_extra', array(
'controllers_by_class' => array(
'Symfony\Cmf\Component\Routing\RedirectRouteInterface' => 'symfony_cmf_routing_extra.redirect_controller:redirectAction',
),
));
Nota
The actual configuration for this association exists as a service, not as part of a config.yml file. Como se explicó antes, puedes utilizar cualquier enfoque.
El componente de enrutado del CMF de Symfony utiliza los componentes predefinidos de Symfony2 para manejar la generación de rutas, así que puedes utilizar los métodos predefinidos para generar tus url, con unas cuantas posibilidades añadidas:
The route generation handles locales as well, see ContentAwareGenerator y regiones.
Como se mencionó arriba, puedes utilizar cualquier proveedor de rutas. El ejemplo en esta sección aplica si utilizas el proveedor de rutas PHPCR-ODM predefinido.
Todas las rutas están localizadas bajo una ruta configurada como la raíz, por ejemplo '/cms/rutas'. A new route can be created in PHP code as follows:
use Symfony\Cmf\Bundle\RoutingExtraBundle\Document\Route;
$route = new Route;
$route->setParent($dm->find(null, '/routes'));
$route->setName('projects');
// enlaza un contenido a la ruta
$content = new Content('my content');
$route->setRouteContent($content);
// ahora configura algún parámetro, 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);
This will give you a document that matches the URL /projects/<number> but also /projects as there is a default for the id parameter.
Your controller can expect the $id parameter as well as the $contentDocument as we set a content on the route. The content could be used to define an intro section that is the same for each project or other shared data. If you don’t need content, you can just not set it in the document.
For more details, see the route document section in the RoutingExtraBundle documentation.
Si en la sección require del archivo composer.json añades el sonata-project/doctrine-phpcr-admin-bundle, los documentos de ruta serán expuestos en el SonataDoctrinePhpcrAdminBundle. Para instrucciones sobre cómo configurar este paquete ve SonataDoctrinePhpcrAdminBundle.
De manera predefinida, use_sonata_admin se pone automáticamente basándose en si SonataDoctrinePhpcrAdminBundle está disponible pero lo puedes desactivar explícitamente si no tienes habilitado sonata, o activarlo explícitamente para conseguir un error si sonata se vuelve inasequible.
Tienes un par de opciones de configuración para la administración. El content_basepath apunta 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
<!-- app/config/config.xml -->
<symfony-cmf-routing-extra:config
use-sonata-admin="auto"
content-basepath="null"
/>
// app/config/config.php
$container->loadFromExtension('symfony_cmf_routing_extra', array(
'use_sonata_admin' => 'auto',
'content_basepath' => null,
));
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. Simply specify symfony_cmf_routing_extra_terms_form_type as the form type name and specify a label and an array with content_ids in the options:
$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.
Para más información sobre el componente Routing del CMF de Symfony, por favor, consulta: