El tutorial Jobeet

12.8. Configuración de la página list

12.8.1. La opción display

La página del listado muestra por defecto todas las columnas del modelo, en el mismo orden en el que se indicaron en el archivo del esquema. La opción display establece las columnas que se muestran y el orden en el que lo hacen:

# apps/backend/modules/category/config/generator.yml
config:
  list:
    title:   Category Management
    display: [=name, slug]

El símbolo = delante de la columna name es una convención que indica que se debe convertir la cadena de texto en un enlace.

La tabla del listado

Figura 12.4 La tabla del listado

A continuación se realiza la misma configuración en el módulo job para hacerlo más fácil de leer:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    title:   Job Management
    display: [company, position, location, url, is_activated, email]

12.8.2. La opción layout

Los listados se pueden mostrar con diferentes layouts. El layout por defecto es tabular, que muestra el valor de cada columna en su propia columna de la tabla. No obstante, en el módulo job sería mejor utilizar el layout stacked, que es el otro layout que incluye Symfony:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    title:   Job Management
    layout:  stacked
    display: [company, position, location, url, is_activated, email]
    params:  |
      %%is_activated%% <small>%%category_id%%</small> - %%company%%
       (<em>%%email%%</em>) is looking for a %%=position%% (%%location%%)

En el layout stacked, cada objeto se representa en una sola cadena de texto, cuyo formato se define en la opción params.

Nota En el ejemplo anterior, la opción display sigue siendo necesaria porque define las columnas por las que el usuario puede reordenar los resultados.

12.8.3. Columnas virtuales

Si se utiliza la configuración anterior, el fragmento %%category_id%% se reemplaza por el valor de la clave primaria de la categoría. Sin embargo, en este caso sería más útil mostrar el nombre de la categoría.

Cuando se hace uso de la notación %%, la variable indicada no tiene que ser obligatoriamente una columna real de la base de datos. Para mostrar el valor de una variable, lo único que necesita el generador de la parte de administración es un método getter en la clase del modelo.

Si queremos mostrar el nombre de una categoría, podemos crear un método llamado getCategoryName() en la clase JobeetJob y reemplazar el fragmento %%category_id%% por %%category_name%%.

Por otra parte, la clase JobeetJob ya dispone de un método llamado getJobeetCategory() y que devuelve el objeto de la categoría relacionada. Por tanto, si utilizas %%jobeet_category%%, ya se va a mostrar el nombre de la categoría, ya que la clase JobeetCategory incluye un método mágico __toString() que convierte un objeto en una cadena de texto.

# apps/backend/modules/job/config/generator.yml
%%is_activated%% <small>%%jobeet_category%%</small> - %%company%%
 (<em>%%email%%</em>) is looking for a %%=position%% (%%location%%)
El layout stacked

Figura 12.5 El layout stacked

12.8.4. La opción sort

Si eres un administrador, seguramente querrás ver las últimas ofertas de trabajo publicadas. Para configurar la columna por la que se ordenan los datos por defecto, incluye la opción sort indicando el nombre de la columna y el tipo de ordenación:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    sort: [expires_at, desc]

12.8.5. La opción max_per_page

El listado incluye por defecto una paginación que muestra 20 elementos en cada página. Este valor se puede modificar con la opción max_per_page:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    max_per_page: 10
Modificando el máximo número de elementos por página

Figura 12.6 Modificando el máximo número de elementos por página

12.8.6. La opción batch_actions

En un listado se puede ejecutar una misma acción sobre varios objetos a la vez. Estas acciones por lotes no se necesitan en el módulo category, por lo que podemos eliminarlas:

# apps/backend/modules/category/config/generator.yml
config:
  list:
    batch_actions: {}
Eliminando las acciones por lotes

Figura 12.7 Eliminando las acciones por lotes

La opción batch_actions define la lista de acciones que se pueden realizar por lotes. Para eliminar esta opción, simplemente se indica un array vacío.

Por defecto cada módulo dispone de una acción de borrado por lotes llamada delete y que define el propio framework. Vamos a suponer que para el módulo job necesitamos además una acción por lotes que permita extender la validez de varias ofertas de trabajo por otros 30 días:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    batch_actions:
      _delete:    ~
      extend:     ~

Las acciones cuyo nombre comienza por _ son acciones que incluye el propio framework. Si refrescas la página en el navegador y seleccionas la acción Extend, Symfony lanza una excepción que indica que debes crear un método llamado executeBatchExtend():

// apps/backend/modules/job/actions/actions.class.php
class jobActions extends autoJobActions
{
  public function executeBatchExtend(sfWebRequest $request)
  {
    $ids = $request->getParameter('ids');

    $jobs = JobeetJobPeer::retrieveByPks($ids);

    foreach ($jobs as $job)
    {
      $job->extend(true);
    }

    $this->getUser()->setFlash('notice', 'The selected jobs have been extended successfully.');

    $this->redirect('@jobeet_job');
  }
}

Las claves primarias de los elementos seleccionados se almacenan en el parámetro ids de la petición. Una vez obtenidas las claves primarias, se ejecuta para cada oferta de trabajo seleccionada el método JobeetJob::extend() con un argumento adicional que permite saltarse la comprobación de la fecha de expiración que realiza ese método.

Actualiza el método extend() pra que tenga en cuenta este nuevo parámetro:

// lib/model/JobeetJob.php
class JobeetJob extends BaseJobeetJob
{
  public function extend($force = false)
  {
    if (!$force && !$this->expiresSoon())
    {
      return false;
    }

    $this->setExpiresAt(time() + 86400 * sfConfig::get('app_active_days'));
    $this->save();

    return true;
  }

  // ...
}

Una vez aumentada la validez de todas las ofertas de trabajo, se redirige al usuario a la portada del módulo job:

Acciones por lotes propias

Figura 12.8 Acciones por lotes propias

12.8.7. La opción object_actions

En el listado de elementos siempre se muestra una columna adicional que contiene las acciones que se pueden realizar sobre un objeto individual. En el módulo category no necesitamos estas acciones porque ya disponemos del nombre de la categoría que es un enlace a la página de modificación de datos y porque tampoco necesitamos borrar una categoría directamente desde el listado:

# apps/backend/modules/category/config/generator.yml
config:
  list:
    object_actions: {}

En el módulo job vamos a dejar todas las acciones existentes y vamos a añadir una nueva acción llamada extend que es similar a la que acabamos de crear como acción por lotes:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    object_actions:
      extend:     ~
      _edit:      ~
      _delete:    ~

Como sucede para las acciones por lotes, las acciones _delete y _edit son acciones que define el propio framework, ya que su nombre empieza por _. Para que la acción extend se pueda utilizar, debemos definir la acción listExtend():

// apps/backend/modules/job/actions/actions.class.php
class jobActions extends autoJobActions
{
  public function executeListExtend(sfWebRequest $request)
  {
    $job = $this->getRoute()->getObject();
    $job->extend(true);

    $this->getUser()->setFlash('notice', 'The selected jobs have been extended successfully.');

    $this->redirect('@jobeet_job');
  }

  // ...
}
Creando una acción propia para los objetos

Figura 12.9 Creando una acción propia para los objetos

12.8.8. La opción actions

En las secciones anteriores se ha mostrado cómo añadir acciones por lotes y acciones que afectan a un solo objeto. Por su parte, la opción actions define las acciones que no utilizan ningún objeto, como la acción para crear un nuevo objeto. A continuación vamos a eliminar la opción new incluida por defecto y vamos a añadir una acción que borre todas las ofertas de trabajo que llevan más de 60 días sin ser activadas por parte del usuario que las insertó:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    actions:
      deleteNeverActivated: { label: Delete never activated jobs }

Hasta ahora, todas las acciones las hemos definido mediante ~, lo que significa que Symfony configura automáticamente esas acciones. Cada acción se puede personalizar pasándole un array de parámetros. La opción label redefine la etiqueta generada por defecto por Symfony.

Por defecto, la acción que se ejecuta cuando pinchas el enlace es el nombre de la acción prefijado con list.

Crea la acción listDeleteNeverActivated en el módulo job:

// apps/backend/modules/job/actions/actions.class.php
class jobActions extends autoJobActions
{
  public function executeListDeleteNeverActivated(sfWebRequest $request)
  {
    $nb = JobeetJobPeer::cleanup(60);

    if ($nb)
    {
      $this->getUser()->setFlash('notice', sprintf('%d never activated jobs have been deleted successfully.', $nb));
    }
    else
    {
      $this->getUser()->setFlash('notice', 'No job to delete.');
    }

    $this->redirect('@jobeet_job');
  }

  // ...
}

Como ya te habrás dado cuenta, hemos reutilizado el método JobeetJobPeer::cleanup() que definimos ayer. Este es otro ejemplo de las posibilidades de reutilización de código que nos brinda el patrón de diseño MVC.

Nota También puedes modificar la acción que se ejecuta mediante el parámetro action:

deleteNeverActivated: { label: Delete never activated jobs, action: foo }
Acciones propias

Figura 12.10 Acciones propias

12.8.9. La opción peer_method

Como muestra la barra de depuración web, se necesitan 14 consultas a la base de datos para mostrar el listado de ofertas de trabajo:

Si pinchas sobre ese número, verás que la mayoría de consultas se utilizan para obtener el nombre de la categoría de cada oferta de trabajo:

Número inicial de consultas

Figura 12.11 Número inicial de consultas

Si quieres reducir el número de consultas, en la opción peer_method puedes modificar el método por defecto que se emplea para obtener las ofertas de trabajo:

# apps/backend/modules/job/config/generator.yml
config:
  list:
    peer_method: doSelectJoinJobeetCategory

El método doSelectJoinJobeetCategory() añade un JOIN entre las tablas job y category para crear de forma automática el objeto de tipo categoría relacionado con cada oferta de trabajo.

Ahora el número de consultas se ha reducido a sólo cuatro:

Número final de consultas

Figura 12.12 Número final de consultas