Más con Symfony

2.6. Personalizando una colección de rutas de objeto

Haciendo uso del framework de enrutamiento, hemos solucionado fácilmente los retos planteados al crear una aplicación como Sympal Builder. A medida que la aplicación crezca, podremos reutilizar las rutas propias en otros módulos del área de administración (para que los clientes puedan por ejemplo gestionar sus galerías de fotos).

Otra razón para crear una colección de rutas propia es la posibilidad de añadir rutas adicionales usadas habitualmente. Imagina que por ejemplo un proyecto utiliza muchos modelos, cada uno de los cuales dispone de una columna is_active. El área de administración debe incluir una forma sencilla de activar o desactivar el valor is_active de un objeto. Para ello, en primer lugar modifica la clase acClientObjectRouteCollection para incluir una nueva ruta a la colección:

// lib/routing/acClientObjectRouteCollection.class.php
protected function generateRoutes()
{
  parent::generateRoutes();

  if (isset($this->options['with_is_active']) && $this->options['with_is_active'])
  {
    $routeName = $this->options['name'].'_toggleActive';

    $this->routes[$routeName] = $this->getRouteForToggleActive();
  }
}

El método ~sfObjectRouteCollection::generateRoutes()~ se invoca al instanciar el objeto de la colección y se encarga de crear todas las rutas necesarias y de incluirlas en la propiedad $routes de la clase. En este caso, derivamos la creación de la ruta a un nuevo método protegido llamado getRouteForToggleActive():

protected function getRouteForToggleActive()
{
  $url = sprintf(
    '%s/:%s/toggleActive.:sf_format',
    $this->options['prefix_path'],
    $this->options['column']
  );

  $params = array(
    'module' => $this->options['module'],
    'action' => 'toggleActive',
    'sf_format' => 'html'
  );

  $requirements = array('sf_method' => 'put');

  $options = array(
    'model' => $this->options['model'],
    'type' => 'object',
    'method' => $this->options['model_methods']['object']
  );

  return new $this->routeClass(
    $url,
    $params,
    $requirements,
    $options
  );
}

El único paso que falta es configurar la colección de rutas en el archivo routing.yml. Como has podido observar, generateRoutes() busca una opción llamada with_is_active antes de añadir la nueva ruta. Incluir esta comprobación permite un mayor control en caso de que se quiera reutilizar la colección acClientObjectRouteCollection más adelante en algún lugar que no necesite la ruta toggleActive:

# apps/frontend/config/routing.yml
pageAdmin:
  class:   acClientObjectRouteCollection
  options:
    model:          Page
    prefix_path:    /pages
    module:         pageAdmin
    with_is_active: true

Ejecuta la tarea app:routes y verifica que existe una nueva ruta llamada toggleActive. Lo único que falta es crear la acción encargada de realizar todo el trabajo. Como es posible que se reutilice en varios módulos esta colección de rutas y su acción asociada, crea un nuevo archivo backendActions.class.php en el directorio apps/backend/lib/action (debes crear a mano este directorio):

// apps/backend/lib/action/backendActions.class.php
class backendActions extends sfActions
{
  public function executeToggleActive(sfWebRequest $request)
  {
    $obj = $this->getRoute()->getObject();

    $obj->is_active = !$obj->is_active;

    $obj->save();

    $this->redirect($this->getModuleName().'/index');
  }
}

Por último, modifica la clase base de pageAdminActions para que herede de esta nueva clase backendActions.

class pageAdminActions extends backendActions
{
  // ...
}

¿Qué es lo que hemos conseguido? Añadir una ruta a la colección de rutas y una acción asociada permite que cualquier módulo pueda incluir automáticamente esta funcionalidad simplemente utilizando la colección acClientObjectRouteCollection y extendiendo la clase backendActions. De esta forma, es posible reutilizar las funcionalidades comunes entre varios módulos diferentes.