Overblog
Editer l'article Suivre ce blog Administration + Créer mon blog

Jouons avec la page Catégorie

Publié le par Penda Jose

Jouons avec la page Catégorie

Jouons avec la page Catégorie

// Commentaires ↓

Dans ce chapitre, nous allons faire la page de catégorie, comme il est décrit dans les scénarios du deuxième chapitre: "L'utilisateur voit une liste de toutes les offres d'emploi de la catégorie triées par date et paginée avec 20 emplois par page".

La route Category

Tout d'abord, nous devons ajouter une route pour utiliser des URL propres pour la page de catégorie. Ajoutez ce code au début du fichier de configuration:

 
  1. # src/Ens/JobeetBundle/Resources/config/routing.yml
  2. EnsJobeetBundle_category:
  3. pattern: /category/{slug}
  4. defaults: { _controller: EnsJobeetBundle:Category:show }

Pour obtenir le jeton (slug) d'une catégorie, nous devons ajouter la méthode getSlug() à notre classe Category:

 
  1. // src/Ens/JobeetBundle/Entity/Category.php
  2. // ...
  3. use Ens\JobeetBundle\Utils\Jobeet;
  4. class Category
  5. {
  6. // ...
  7. public function getSlug()
  8. {
  9. return Jobeet::slugify($this->getName());
  10. }
  11. // ...
  12. }

Le lien de la catégorie

Maintenant, modifiez le template index.html.twig du contrôleur Job et ajoutez le lien vers la page catégorie:

 
  1. <!-- src/Ens/JobeetBundle/Resources/views/Job/index.html.twig -->
  2. <!-- some HTML code -->
  3. <h1><a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug }) }}">{{ category.name }}</a></h1>
  4. <!-- some HTML code -->
  5. </table>
  6. {% if category.morejobs %}
  7. <div class="more_jobs">
  8. and <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug }) }}">{{ category.morejobs }}</a>
  9. more...
  10. </div>
  11. {% endif %}
  12. </div>
  13. {% endfor %}
  14. </div>
  15. {% endblock %}

Dans le template ci-dessus, nous avons utilisé category.morejobs, nous allons donc le définir:

 
  1. // src/Ens/JobeetBunlde/Entity/Category.php
  2. // ...
  3. private $more_jobs;
  4. // ...
  5. public function setMoreJobs($jobs)
  6. {
  7. $this->more_jobs = $jobs >= 0 ? $jobs : 0;
  8. }
  9. public function getMoreJobs()
  10. {
  11. return $this->more_jobs;
  12. }
  13. // ...

La propriété more_jobs prendra comme valeur le nombre d'offres actives pour la catégorie moins le nombre d'offres figurant sur la page d'accueil. Maintenant, dans JobController, nous avons besoin de définir la valeur more_jobs pour chaque catégorie:

 
  1. // src/Ens/JobeetBundle/Controller/JobController.php
  2. // ...
  3. public function indexAction()
  4. {
  5. $em = $this->getDoctrine()->getEntityManager();
  6. $categories = $em->getRepository('EnsJobeetBundle:Category')->getWithJobs();
  7. foreach($categories as $category)
  8. {
  9. $category->setActiveJobs($em->getRepository('EnsJobeetBundle:Job')->getActiveJobs($category->getId(), $this->container->getParameter('max_jobs_on_homepage')));
  10. $category->setMoreJobs($em->getRepository('EnsJobeetBundle:Job')->countActiveJobs($category->getId()) - $this->container->getParameter('max_jobs_on_homepage'));
  11. }
  12. return $this->render('EnsJobeetBundle:Job:index.html.twig', array(
  13. 'categories' => $categories
  14. ));
  15. }
  16. // ...

La fonction countActiveJobs doit être ajoutée dans JobRepository:

 
  1. // src/Ens/JobeetBundle/Repository/JobRepository.php
  2. // ...
  3. public function countActiveJobs($category_id = null)
  4. {
  5. $qb = $this->createQueryBuilder('j')
  6. ->select('count(j.id)')
  7. ->where('j.expires_at > :date')
  8. ->setParameter('date', date('Y-m-d H:i:s', time()));
  9. if($category_id)
  10. {
  11. $qb->andWhere('j.category = :category_id')
  12. ->setParameter('category_id', $category_id);
  13. }
  14. $query = $qb->getQuery();
  15. return $query->getSingleScalarResult();
  16. }
  17. // ...

Maintenant, vous devriez voir le résultat dans votre navigateur:

Création du contrôleur Category

Il est maintenant temps de créer le contrôleur Category. Créez un nouveau fichier CategoryController.php dans votre répertoire Controller:

 
  1. // src/Ens/JobeetBundle/Controller/CategoryController.php
  2. namespace Ens\JobeetBundle\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  4. use Ens\JobeetBundle\Entity\Category;
  5. /**
  6. * Category controller.
  7. *
  8. */
  9. class CategoryController extends Controller
  10. {
  11. }

Nous pourrions utiliser la commande doctrine:generate:crud comme nous l'avons fait pour JobController, mais nous n'aurons pas besoin de 90% du code généré, donc nous pouvons juste créer un nouveau contrôleur à partir de zéro.

Mettre à jour la Base De Données

Nous avons besoin d'ajouter une colonne slug dans la table category et une entrée lifecycleCallbacks pour définir la valeur de cette colonne:

 
  1. # src/Ens/JobeetBundle/Resources/config/doctrine/Category.orm.yml
  2. Ens\JobeetBundle\Entity\Category:
  3. type: entity
  4. repositoryClass: Ens\JobeetBundle\Repository\CategoryRepository
  5. table: category
  6. id:
  7. id:
  8. type: integer
  9. generator: { strategy: AUTO }
  10. fields:
  11. name:
  12. type: string
  13. length: 255
  14. unique: true
  15. slug:
  16. type: string
  17. length: 255
  18. unique: true
  19. oneToMany:
  20. jobs:
  21. targetEntity: Job
  22. mappedBy: category
  23. category_affiliates:
  24. targetEntity: CategoryAffiliate
  25. mappedBy: category
  26. lifecycleCallbacks:
  27. prePersist: [ setSlugValue ]
  28. preUpdate: [ setSlugValue ]

Retirez de l'entité Category (src/Ens/JobeetBundle/Entity/category.php) la méthode getSlug que nous avons créé précédemment et exécutez la commande Doctrine pour mettre à jour la classe d'entité Category:

php app/console doctrine:generate:entities EnsJobeetBundle

Maintenant vous devriez avoir ce qui suit ajouté à Category.php:

 
  1. // srr/Ens/JobeetBundle/Entity/Category.php
  2. // ...
  3. /**
  4. * @var string $slug
  5. */
  6. private $slug;
  7. /**
  8. * Set slug
  9. *
  10. * @param string $slug
  11. */
  12. public function setSlug($slug)
  13. {
  14. $this->slug = $slug;
  15. }
  16. /**
  17. * Get slug
  18. *
  19. * @return string
  20. */
  21. public function getSlug()
  22. {
  23. return $this->slug;
  24. }
  25. /**
  26. * @ORM\prePersist
  27. */
  28. public function setSlugValue()
  29. {
  30. // Add your code here
  31. }

Changez la fonction setSlugValue avec ce qui suit:

 
  1. public function setSlugValue()
  2. {
  3. $this->slug = Jobeet::slugify($this->getName());
  4. }

Maintenant, nous devons supprimer la BDD et la recréer avec la nouvelle colonne de la table category et charger les fixtures:

 
  1. php app/console doctrine:database:drop --force
  2. php app/console doctrine:database:create
  3. php app/console doctrine:schema:update --force
  4. php app/console doctrine:fixtures:load

La page catégorie

Tout est maintenant en place pour créer la méthode showAction(). Ajoutez le code suivant au fichier CategoryController.php:

 
  1. // src/Ens/JobeetBundle/Controller/CategoryController.php
  2. // ...
  3. public function showAction($slug)
  4. {
  5. $em = $this->getDoctrine()->getEntityManager();
  6. $category = $em->getRepository('EnsJobeetBundle:Category')->findOneBySlug($slug);
  7. if (!$category) {
  8. throw $this->createNotFoundException('Unable to find Category entity.');
  9. }
  10. $category->setActiveJobs($em->getRepository('EnsJobeetBundle:Job')->getActiveJobs($category->getId()));
  11. return $this->render('EnsJobeetBundle:Category:show.html.twig', array(
  12. 'category' => $category,
  13. ));
  14. }
  15. // ...

La dernière étape consiste à créer le template show.html.twig:

 
  1. <!-- src/Ens/JobeetBundle/Resources/view/Category/show.html.twig -->
  2. {% extends 'EnsJobeetBundle::layout.html.twig' %}
  3. {% block title %}
  4. Jobs in the {{ category.name }} category
  5. {% endblock %}
  6. {% block stylesheets %}
  7. {{ parent() }}
  8. <link rel="stylesheet" href="{{ asset('bundles/ensjobeet/css/jobs.css') }}" type="text/css" media="all" />
  9. {% endblock %}
  10. {% block content %}
  11. <div class="category">
  12. <div class="feed">
  13. <a href="">Feed</a>
  14. </div>
  15. <h1>{{ category.name }}</h1>
  16. </div>
  17. <table class="jobs">
  18. {% for entity in category.activejobs %}
  19. <tr class="{{ cycle(['even', 'odd'], loop.index) }}">
  20. <td class="location">{{ entity.location }}</td>
  21. <td class="position">
  22. <a href="{{ path('ens_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}">
  23. {{ entity.position }}
  24. </a>
  25. </td>
  26. <td class="company">{{ entity.company }}</td>
  27. </tr>
  28. {% endfor %}
  29. </table>
  30. {% endblock %}

Inclure d'autres templates Twig

Notez que nous avons copié/collé la balise <table> qui crée une liste d'offres à partir du template des offres index.html.twig. C'est une mauvaise solution. Lorsque vous avez besoin de réutiliser une partie d'un template, vous devez créer un nouveau template Twig avec ce code et l'inclure où vous en avez besoin.

Créez le fichier list.html.twig:

 
  1. <!-- src/Ens/JobeetBundle/Resources/views/Job/list.html.twig -->
  2. <table class="jobs">
  3. {% for entity in jobs %}
  4. <tr class="{{ cycle(['even', 'odd'], loop.index) }}">
  5. <td class="location">{{ entity.location }}</td>
  6. <td class="position">
  7. <a href="{{ path('ens_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}">
  8. {{ entity.position }}
  9. </a>
  10. </td>
  11. <td class="company">{{ entity.company }}</td>
  12. </tr>
  13. {% endfor %}
  14. </table>

Vous pouvez inclure un template à l'aide de la balise {% include%}. Remplacez le code HTML <table> dans les deux templates avec la balise include:

 
  1. <!-- in src/Ens/JobeetBundle/Resources/view/Job/index.html.twig -->
  2. {% include 'EnsJobeetBundle:Job:list.html.twig' with {'jobs': category.activejobs} %}
  3. <!-- in src/Ens/JobeetBundle/Resources/view/Category/show.html.twig -->
  4. {% include 'EnsJobeetBundle:Job:list.html.twig' with {'jobs': category.activejobs} %}

Pagination de listes

Au moment où ce tutoriel est écrit, Symfony2 ne fournit pas de réels bons outils de pagination "out of the box". Afin de résoudre ce problème, nous allons utiliser l'ancienne méthode classique.

Tout d'abord, nous allons ajouter un paramètre page à la route EnsJobeetBundle_category. Le paramètre page aura une valeur par défaut de 1, donc il ne sera pas obligatoire:

 
  1. # src/Ens/JobeetBundle/Resources/config/routing.yml
  2. EnsJobeetBundle_category:
  3. pattern: /category/{slug}/{page}
  4. defaults: { _controller: EnsJobeetBundle:Category:show, page: 1 }
  5. # ...

Le nombre d'offres sur chaque page sera défini comme un paramètre personnalisé dans le fichier app/config/config.yml:

 
  1. # app/config/config.yml
  2. # ...
  3. parameters:
  4. max_jobs_on_homepage: 10
  5. max_jobs_on_category: 20

Modifiez la méthode getActiveJobs de JobRepository afin d'inclure un paramètre $offset pour être utilisé par Doctrine lors de la récupération des offres:

 
  1. // src/Ens/JobeetBundle/Repository/JobRepository.php
  2. // ...
  3. public function getActiveJobs($category_id = null, $max = null, $offset = null)
  4. {
  5. $qb = $this->createQueryBuilder('j')
  6. ->where('j.expires_at > :date')
  7. ->setParameter('date', date('Y-m-d H:i:s', time()))
  8. ->orderBy('j.expires_at', 'DESC');
  9. if($max)
  10. {
  11. $qb->setMaxResults($max);
  12. }
  13. if($offset)
  14. {
  15. $qb->setFirstResult($offset);
  16. }
  17. if($category_id)
  18. {
  19. $qb->andWhere('j.category = :category_id')
  20. ->setParameter('category_id', $category_id);
  21. }
  22. $query = $qb->getQuery();
  23. return $query->getResult();
  24. }
  25. // ...

Modifiez l'action showAction de CategoryController avec ce qui suit:

 
  1. // src/Ens/JobeetBundle/Controller/CategoryController.php
  2. // ...
  3. public function showAction($slug, $page)
  4. {
  5. $em = $this->getDoctrine()->getEntityManager();
  6. $category = $em->getRepository('EnsJobeetBundle:Category')->findOneBySlug($slug);
  7. if (!$category) {
  8. throw $this->createNotFoundException('Unable to find Category entity.');
  9. }
  10. $total_jobs = $em->getRepository('EnsJobeetBundle:Job')->countActiveJobs($category->getId());
  11. $jobs_per_page = $this->container->getParameter('max_jobs_on_category');
  12. $last_page = ceil($total_jobs / $jobs_per_page);
  13. $previous_page = $page > 1 ? $page - 1 : 1;
  14. $next_page = $page < $last_page ? $page + 1 : $last_page;
  15. $category->setActiveJobs($em->getRepository('EnsJobeetBundle:Job')->getActiveJobs($category->getId(), $jobs_per_page, ($page - 1) * $jobs_per_page));
  16. return $this->render('EnsJobeetBundle:Category:show.html.twig', array(
  17. 'category' => $category,
  18. 'last_page' => $last_page,
  19. 'previous_page' => $previous_page,
  20. 'current_page' => $page,
  21. 'next_page' => $next_page,
  22. 'total_jobs' => $total_jobs
  23. ));
  24. }

Enfin, nous allons mettre à jour le template:

 
  1. <!-- src/Ens/JobeetBundle/Resources/views/Category/show.html.twig -->
  2. {% extends 'EnsJobeetBundle::layout.html.twig' %}
  3. {% block title %}
  4. Jobs in the {{ category.name }} category
  5. {% endblock %}
  6. {% block stylesheets %}
  7. {{ parent() }}
  8. <link rel="stylesheet" href="{{ asset('bundles/ensjobeet/css/jobs.css') }}" type="text/css" media="all" />
  9. {% endblock %}
  10. {% block content %}
  11. <div class="category">
  12. <div class="feed">
  13. <a href="">Feed</a>
  14. </div>
  15. <h1>{{ category.name }}</h1>
  16. </div>
  17. {% include 'EnsJobeetBundle:Job:list.html.twig' with {'jobs': category.activejobs} %}
  18. {% if last_page > 1 %}
  19. <div class="pagination">
  20. <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug, 'page': 1 }) }}">
  21. <img src="{{ asset('bundles/ensjobeet/images/first.png') }}" alt="First page" title="First page" />
  22. </a>
  23. <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug, 'page': previous_page }) }}">
  24. <img src="{{ asset('bundles/ensjobeet/images/previous.png') }}" alt="Previous page" title="Previous page" />
  25. </a>
  26. {% for page in 1..last_page %}
  27. {% if page == current_page %}
  28. {{ page }}
  29. {% else %}
  30. <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug, 'page': page }) }}">{{ page }}</a>
  31. {% endif %}
  32. {% endfor %}
  33. <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug, 'page': next_page }) }}">
  34. <img src="{{ asset('bundles/ensjobeet/images/next.png') }}" alt="Next page" title="Next page" />
  35. </a>
  36. <a href="{{ path('EnsJobeetBundle_category', { 'slug': category.slug, 'page': last_page }) }}">
  37. <img src="{{ asset('bundles/ensjobeet/images/last.png') }}" alt="Last page" title="Last page" />
  38. </a>
  39. </div>
  40. {% endif %}
  41. <div class="pagination_desc">
  42. <strong>{{ total_jobs }}</strong> jobs in this category
  43. {% if last_page > 1 %}
  44. - page <strong>{{ current_page }}/{{ last_page }}</strong>
  45. {% endif %}
  46. </div>
  47. {% endblock %}

Le résultat:

Commenter cet article