Espacio Daycry - Espacio de programación

  • Inicio
  • Categorias
    • - Codeigniter
    • - Symfony
    • - HTML5
    • - Linux / Ubuntu
    • - PHP
    • - Jquery
  • PortFolio - Proyectos Codeiniter
    • - Encuestas Online
    • - Estadísticas - GLPI
    • - Gestión de colas
    • - Web Service - REST

jueves, 21 de agosto de 2014

Symfony - Día 7: Jugando con la Página de Categoría

Posted by daycry at 15:13 Labels: symfony
Ayer expandiste tus conocimiento de symfony sobre un montón de áreas diferentes: consultas con Doctrine, archivos de datos, enrutamiento, depuración, y personalizar la configuración. Y terminamos con un pequeño desafío para hoy.
Espero que hayas trabajado en la Página de Categoría de Jobeet así el tutorial de hoy será mucho más productivo para ti.
¿Listo? Vamos a hablar acerca de una posible implementación.

La Ruta Category

Primero, necesitamos agregar una ruta para definir una URL amigable para la página de la categoría. Agrégalo al inicio del archivo de enrutamiento:
# apps/frontend/config/routing.yml
category:
  url:      /category/:slug
  class:    sfDoctrineRoute
  param:    { module: category, action: show }
  options:  { model: JobeetCategory, type: object }
Si vas a comenzar la implementación de una nueva funcionalidad, es una buena práctica primero pensar acerca de la URL y crear la ruta asociada. Y esto es obligatorio si quitas las reglas de enrutamiento por defecto.
Como slug no es una columna de la tabla category, necesitamos para agregar un método virtual en JobeetCategory para que la ruta funcione:
Un ruta puede usar cualquier columna de su objeto asociado como parámetro. También puede usa cualquier otro valor si hay un método asociado definido en la clase del objeto. Debido a que el parámetro slug no tiene una columna correspondiente en la tabla category, necesitamos agregar un método de acceso virtual en JobeetCategory para que la ruta funcione:
// lib/model/doctrine/JobeetCategory.class.php
public function getSlug()
{
  return Jobeet::slugify($this->getName());
}

El Enlace Categoría

Ahora, edita la plantilla indexSuccess.php del módulo job para agregar el enlace a la página de la categoría:
<!-- some HTML code -->
 
        <h1>
          <?php echo link_to($category, 'category', $category) ?>
        </h1>
 
<!-- some HTML code -->
 
      </table>
 
      <?php if (($count = $category->countActiveJobs() -
          sfConfig::get('app_max_jobs_on_homepage')) > 0): ?>
        <div class="more_jobs">
          and <?php echo link_to($count, 'category', $category) ?>
          more...
        </div>
      <?php endif; ?>
    </div>
  <?php endforeach; ?>
</div>
Solo agregamos el enlace si hay más de 10 puestos de trabajo a mostrar para la categoría actual. El enlace tiene el número de puestos de trabajo no mostrados. Para que esta plantilla funcione, necesitamos agregar el método countActiveJobs() a JobeetCategory:
// lib/model/doctrine/JobeetCategory.class.php
public function countActiveJobs()
{
  $q = Doctrine_Query::create()
    ->from('JobeetJob j')
    ->where('j.category_id = ?', $this->getId());
 
  return Doctrine_Core::getTable('JobeetJob')->countActiveJobs($q);
}
El método countActiveJobs() emplea un método countActiveJobs() que aún no existe enJobeetJobTable. Reemplaza el contenido del archivo JobeetJobTable.php con el siguiente código: [php] // lib/model/doctrine/JobeetJobTable.class.php class JobeetJobTable extends Doctrine_Table { public function retrieveActiveJob(Doctrine_Query $q) { return $this->addActiveJobsQuery($q)->fetchOne(); }
  public function getActiveJobs(Doctrine_Query $q = null)
  {
    return $this->addActiveJobsQuery($q)->execute();
  }

  public function countActiveJobs(Doctrine_Query $q = null)
  {
    return $this->addActiveJobsQuery($q)->count();
  }

  public function addActiveJobsQuery(Doctrine_Query $q = null)
  {
    if (is_null($q))
    {
      $q = Doctrine_Query::create()
        ->from('JobeetJob j');
    }

    $alias = $q->getRootAlias();

    $q->andWhere($alias . '.expires_at > ?', date('Y-m-d h:i:s', time()))
      ->addOrderBy($alias . '.expires_at DESC');

    return $q;
  }
}
Como puedes ver por ti mismo, tenemos que refactorizar todo el código de JobeetJobTable para introducir un nuevo método compartido addActiveJobsQuery() para hacer el código más DRY (Don't Repeat Yourself).
La primera vez, un trozo de código es re-usado, copiando el código puede ser suficiente. Pero si encuentras otra función para él necesitas refactorizar para reutilizar la función o método, como hemos hecho aquí.
En el método countActiveJobs(), en vez de usar execute() y recién contar el número de resultados, usamos el método mas rápido count().
Hemos cambiado un montón de archivos, recién para esta simple funcionalidad. Pero cada vez que debas agregar algún código, tenemos que tratar de ponerlo en la capa correcta de la aplicación y también tratar de hace el código más reusable. En el proceso, tenemos que rectorizar algún código existente. Ese es el típico workflow cuando trabajamos en un proyecto symfony.
Página de Inicio

Creación del Módulo Category

Es hora de crear el módulo category:
$ php symfony generate:module frontend category
Si has creado un módulo, probablemente has utilizado el doctrine:generate-module. Eso está bien, pero como no es necesario el 90% del código generado, usa generate:module para crear un módulo vacío.
¿Por qué no añadir una acción category al módulo job? Podríamos, pero como el tema principal de la página de categoría es una categoría, se siente más natural crear un módulo dedicado category.
Al acceder a la página de categoría, la ruta category tendrá que encontrar la categoría asociada con la variable slug de la petición. Pero como slug no se almacena en la base de datos, y porque no podemos deducir el nombre de la categoría del slug, no hay forma de encontrar la categoría asociada con el slug.

Actualizar la Base de Datos

Tenemos que añadir una columna slug para la tabla category:
Esta columna slug puede ser tomada con cuidado por un comportamiento Doctrine llamadoSluggable. Simplemente necesitamos habilitar el comportamiento sobre nuestro modeloJobeetCategory y este se encargará de todo por tí.
# config/doctrine/schema.yml
JobeetCategory:
  actAs:
    Timestampable: ~
    Sluggable:
      fields: [name]
  columns:
    name:
      type: string(255)
      notnull:  true
Ahora que slug es una columna real, es necesario eliminar el método getSlug() deJobeetCategory.
La configuración de la columna slug es tomada automáticamente cuando guardas un registro. El slug es armado usando el valor del campo name y se lo da al objeto.
Usa la tarea doctrine:build --all --and-load para actualizar las tablas de la base de datos, y llenar la base de datos con nuestros datos:
$ php symfony doctrine:build --all --and-load --no-confirmation
Tenemos ahora todo en su lugar para crear el método executeShow(). Reemplaza el contenido del archivo de acciones category con el siguiente código:
// apps/frontend/modules/category/actions/actions.class.php
class categoryActions extends sfActions
{
  public function executeShow(sfWebRequest $request)
  {
    $this->category = $this->getRoute()->getObject();
  }
}
Debido a que quitamos el método generado executeIndex(), también puedes quitar la platilla automáticamente generada indexSuccess.php(apps/frontend/modules/category/templates/indexSuccess.php).
El último paso es crear la plantilla showSuccess.php:
// apps/frontend/modules/category/templates/showSuccess.php
<?php use_stylesheet('jobs.css') ?>
 
<?php slot('title', sprintf('Jobs in the %s category', $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>

Elementos Parciales o Partials

Nota del Traductor Los Partials o Parciales, son elementos que se usan en las plantillas, haciendo uso del helper include_partial(), es por eso que su traducción literal no es muy amigable, ya que debemos pensar no en parcial sino en porción (snippet de código de la capa Vista)
Observa que hemos copiado y pegado la etiqueta <table> que crear una lista de puestos de trabajo en la plantilla indexSuccess.php. Eso esta mal. Es tiempo para aprender un nuevo truco. Cuando necesites volver a utilizar una parte de una plantilla, lo que necesitas es crear unpartial. Un partial es un snippet de código de plantilla que puede ser compartido entre varias plantillas. Un partial es sólo otra plantilla que comienza con un guión bajo (_).
Crea el archivo _list.php:
// apps/frontend/modules/job/templates/_list.php
<table class="jobs">
  <?php foreach ($jobs 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>
Puedes incluir un partial utilizando el helper include_partial():
<?php include_partial('job/list', array('jobs' => $jobs)) ?>
El primer argumento de include_partial() es el nombre del partial (hecho del nombre del módulo, una /, y el nombre del partial sin el _). El segundo argumento es un array de las variables a pasar al partial.
¿Por qué no utilizar el método include() incluído en PHP en lugar del helperinclude_partial()? La principal diferencia entre los dos es el soporte de cache incluído del helper include_partial().
Reemplaza el HTML <table> de ambas plantillas con la llamada a include_partial():
// in apps/frontend/modules/job/templates/indexSuccess.php
<?php include_partial('job/list', array('jobs' => $category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')))) ?>
 
// in apps/frontend/modules/category/templates/showSuccess.php
<?php include_partial('job/list', array('jobs' => $category->getActiveJobs())) ?>

Lista Paginada

De los requisitos del día 2:
"La lista es paginada, con 20 puestos de trabajo por página."
Para paginar una lista de un Objetos Doctrine, symfony proporciona una clase dedicada a ello:sfDoctrinePager. En la acción category en lugar de pasar los objetos (jobs) de los puestos de trabajo a la plantilla, pasamos un paginador:
// apps/frontend/modules/category/actions/actions.class.php
public function executeShow(sfWebRequest $request)
{
  $this->category = $this->getRoute()->getObject();
 
  $this->pager = new sfDoctrinePager(
    'JobeetJob',
    sfConfig::get('app_max_jobs_on_category')
  );
  $this->pager->setQuery($this->category->getActiveJobsQuery());
  $this->pager->setPage($request->getParameter('page', 1));
  $this->pager->init();
}
El método sfRequest::getParameter() toma un valor por defecto como segundo argumento. En la acción anterior, si el parámetro de la petición page no existe, entonces getParameter() devolverá 1.
El constructor de sfDoctrinePager tiene una clase del modelo y el número máximo de elementos a regresar por página. Añade este último valor a tu archivo de configuración:
# apps/frontend/config/app.yml
all:
  active_days:          30
  max_jobs_on_homepage: 10
  max_jobs_on_category: 20
El método sfDoctrinePager::setQuery() toma un objeto Doctrine_Query para utilizarlo a la hora de seleccionar los elementos de la base de datos.
Agrega el método getActiveJobsCriteria():
// lib/model/doctrine/JobeetCategory.class.php
public function getActiveJobsQuery()
{
  $q = Doctrine_Query::create()
    ->from('JobeetJob j')
    ->where('j.category_id = ?', $this->getId());
 
  return Doctrine_Core::getTable('JobeetJob')->addActiveJobsQuery($q);
}
Ahora que tenemos el método getActiveJobsQuery(), podemos refactorizar otros métodos deJobeetCategory para usarlos:
// lib/model/doctrine/JobeetCategory.class.php
public function getActiveJobs($max = 10)
{
  $q = $this->getActiveJobsQuery()
    ->limit($max);
 
  return $q->execute();
}
 
public function countActiveJobs()
{
  return $this->getActiveJobsQuery()->count();
}
Por último, vamos a actualizar la plantilla:
<!-- apps/frontend/modules/category/templates/showSuccess.php -->
<?php use_stylesheet('jobs.css') ?>
 
<?php slot('title', sprintf('Jobs in the %s category', $category->getName())) ?>
 
<div class="category">
  <div class="feed">
    <a href="">Feed</a>
  </div>
  <h1><?php echo $category ?></h1>
</div>
 
<?php include_partial('job/list', array('jobs' => $pager->getResults())) ?>
 
<?php if ($pager->haveToPaginate()): ?>
  <div class="pagination">
    <a href="<?php echo url_for('category', $category) ?>?page=1">
      <img src="/legacy/images/first.png" alt="First page" title="First page" />
    </a>
 
    <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getPreviousPage() ?>">
      <img src="/legacy/images/previous.png" alt="Previous page" title="Previous page" />
    </a>
 
    <?php foreach ($pager->getLinks() as $page): ?>
      <?php if ($page == $pager->getPage()): ?>
        <?php echo $page ?>
      <?php else: ?>
        <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $page ?>"><?php echo $page ?></a>
      <?php endif; ?>
    <?php endforeach; ?>
 
    <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getNextPage() ?>">
      <img src="/legacy/images/next.png" alt="Next page" title="Next page" />
    </a>
 
    <a href="<?php echo url_for('category', $category) ?>?page=<?php echo $pager->getLastPage() ?>">
      <img src="/legacy/images/last.png" alt="Last page" title="Last page" />
    </a>
  </div>
<?php endif; ?>
 
<div class="pagination_desc">
  <strong><?php echo $pager->getNbResults() ?></strong> jobs in this category
 
  <?php if ($pager->haveToPaginate()): ?>
    - page <strong><?php echo $pager->getPage() ?>/<?php echo $pager->getLastPage() ?></strong>
  <?php endif; ?>
</div>
La mayor parte de este código se refiere a los enlaces a otras páginas. Aquí está la lista de métodos sfDoctrinePager usados en esta plantilla:
  • getResults(): Devuelve un array objetos Doctrine para la página actual
  • getNbResults(): Devuelve el número total de resultados
  • haveToPaginate(): Devuelve true si hay más de una página
  • getLinks(): Devuelve una lista de los enlaces de la página a mostrar
  • getPage(): Devuelve el número de la página actual
  • getPreviousPage(): Devuelve el número de la página anterior
  • getNextPage(): Devuelve el número de la siguiente página
  • getLastPage(): Devuelve el número de la última página
Paginación

Referencia: http://symfony.com/legacy/doc/jobeet/1_4/es/07?orm=Doctrine
Tweet

Related Posts

  • Symfony - Día 8: Pruebas Unitarias
    Symfony - Día 8: Pruebas Unitarias
  • Symfony - Día 6: Más acerca del Modelo
    Symfony - Día 6: Más acerca del Modelo
  • Symfony - Día 5: El Enrutamiento
    Symfony - Día 5: El Enrutamiento
  • Symfony - Día 4: El Controlador y la Vista
    Symfony - Día 4: El Controlador y la Vista

No hay comentarios :

Publicar un comentario

Entrada más reciente Entrada antigua Inicio
Suscribirse a: Enviar comentarios ( Atom )

Sígueme en las Redes Sociales



Follow @daycry9



Donaciones

Suscribirse a

Entradas
Atom
Entradas
Comentarios
Atom
Comentarios

Datos personales

daycry
Ver todo mi perfil

Entradas populares

  • Crear archivos PHP ejecutables por terminal UBUNTU
    En este apartado vamos a explicar como ejercutar archivos PHP a través del terminal de Ubuntu. Lo primero que tendríamos que hacer es inst...
  • Pâginación PHP con Librería Zebra Pagination
    En este blog voy a comentar un tema que se utilizan en casi todas las páginas web que existen, y es el tema de la paginación. La paginaci...
  • PHPExcel - Codeigniter
    Este post trata de la integración de la librería PHPExcel en Codeigniter, aunque se podría aplicar a cualquier librería, como por ejemplo mP...
  • PHP- Operaciones con fechas - Sumar Horas, minutos y segundos
    Ejemplo para añadir o sumar un número determinado de hora/s, minuto/s, segundo/s a una fecha en php. Con la función strtotime se puede ...
  • Codeigniter - Múltiples conexiones a base de datos
    Este tema es uno de los temas primordiales sobre el framework Codeigniter, ya que en alguna ocación nos hemos visto obligados a recoger dato...

© Espacio Daycry - Espacio de programación 2013 . Powered by Bootstrap , Blogger templates and RWD Testing Tool