El tutorial Jobeet

6.7. Mostrando las categorías en la portada

Otro de los requerimientos que establecimos durante el segundo día era: "las ofertas se agrupan por categoría y se ordenan por fecha de publicación (primero se muestran los trabajos más recientes)".

Hasta ahora no hemos tenido en cuenta la categoría de cada oferta de trabajo, aunque los requerimientos de la aplicación indican que la portada muestra las ofertas de trabajo agrupadas por categoría. En primer lugar debemos obtener todas las categorías que tienen al menos una oferta de trabajo activa.

Abre la clase JobeetCategoryPeer y añade el siguiente método llamado getWithJobs():

// lib/model/JobeetCategoryPeer.php
class JobeetCategoryPeer extends BaseJobeetCategoryPeer
{
  static public function getWithJobs()
  {
    $criteria = new Criteria();
    $criteria->addJoin(self::ID, JobeetJobPeer::CATEGORY_ID);
    $criteria->add(JobeetJobPeer::EXPIRES_AT, time(), Criteria::GREATER_THAN);
    $criteria->setDistinct();

    return self::doSelect($criteria);
  }
}

El método Criteria::addJoin() añade una condición de tipo JOIN en la sentencia SQL generada. Por defecto la condición JOIN se añade a la condición WHERE. Si quieres modificar el tipo de JOIN, utiliza uno de los siguientes valores como tercer argumento:Criteria::LEFT_JOIN, Criteria::RIGHT_JOIN y Criteria::INNER_JOIN.

Ahora actualiza la acción index para que utilice el nuevo método:

// apps/frontend/modules/job/actions/actions.class.php
public function executeIndex(sfWebRequest $request)
{
  $this->categories = JobeetCategoryPeer::getWithJobs();
}

En la plantilla asociada a la acción ahora tenemos que iterar por todas las categorías para mostrar sus ofertas de trabajo activas:

// apps/frontend/modules/job/templates/indexSuccess.php
<?php use_stylesheet('jobs.css') ?>

<div id="jobs">
  <?php foreach ($categories as $category): ?>
    <div class="category_<?php echo Jobeet::slugify($category->getName()) ?>">
      <div class="category">
        <div class="feed">
          <a href="">Feed</a>
        </div>
        <h1><?php echo $category ?></h1>
      </div>

      <table class="jobs">
        <?php foreach ($category->getActiveJobs() as $i => $job): ?>
          <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
            <td class="location">
              <?php echo $job->getLocation() ?>
            </td>
            <td class="position">
              <?php echo link_to($job->getPosition(), 'job_show_user', $job) ?>
            </td>
            <td class="company">
              <?php echo $job->getCompany() ?>
            </td>
          </tr>
        <?php endforeach; ?>
      </table>
    </div>
  <?php endforeach; ?>
</div>

Nota La plantilla anterior utiliza echo $category para mostrar el nombre de la categoría. ¿Te parece extraño? Teniendo en cuenta que $category es un objeto, ¿cómo es posible que echo muestre mágicamente el nombre de la categoría? La respuesta se encuentra en el tutorial del día 3, donde definimos métodos mágicos __toString() en todas las clases del modelo.

Para que la plantilla anterior funcione correctamente, debemos añadir el método getActiveJobs() en la clase JobeetCategory:

// lib/model/JobeetCategory.php
public function getActiveJobs()
{
  $criteria = new Criteria();
  $criteria->add(JobeetJobPeer::CATEGORY_ID, $this->getId());

  return JobeetJobPeer::getActiveJobs($criteria);
}

En la llamada al método add(), hemos omitido el tecer argumento porque Criteria::EQUAL es el valor por defecto.

El método JobeetCategory::getActiveJobs() utiliza a su vez el método JobeetJobPeer::getActiveJobs() para obtener las ofertas de trabajo activas para la categoría indicada.

Cuando se invoca el método JobeetJobPeer::getActiveJobs(), queremos hacer la condición más restrictiva pasándole una categoría. En lugar de pasar el objeto de la categoría actual, hemos decidido pasarle un objeto de tipo Criteria, ya que es la mejor forma de encapsular una condición genérica.

Por tanto, el método getActiveJobs() tiene que que tenerlo en cuenta y debe fusionar el objeto Criteria que se le pasa y su propio Criteria. Como Criteria es un objeto, el código resultante es muy sencillo:

// lib/model/JobeetJobPeer.php
static public function getActiveJobs(Criteria $criteria = null)
{
  if (is_null($criteria))
  {
    $criteria = new Criteria();
  }

  $criteria->add(JobeetJobPeer::EXPIRES_AT, time(), Criteria::GREATER_THAN);
  $criteria->addDescendingOrderByColumn(self::EXPIRES_AT);

  return self::doSelect($criteria);
}