{"id":2212,"date":"2013-08-13T09:29:42","date_gmt":"2013-08-13T09:29:42","guid":{"rendered":"https:\/\/intelligentbee.com\/blog\/?p=2212"},"modified":"2024-09-30T07:54:05","modified_gmt":"2024-09-30T07:54:05","slug":"symfony2-jobeet-day-7-playing-with-the-category-page","status":"publish","type":"post","link":"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/","title":{"rendered":"Symfony2 Jobeet Day 7: Playing With the Category Page"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_68_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title \" >Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/#_This_article_is_part_of_the_original_Jobeet_Tutorial_created_by_Fabien_Potencier_for_Symfony_14_Category_page_in_jobeet\" title=\"* This article is part of the original Jobeet Tutorial, created by Fabien Potencier, for Symfony 1.4. \nCategory page in jobeet\">* This article is part of the original Jobeet Tutorial, created by Fabien Potencier, for Symfony 1.4. \nCategory page in jobeet<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/#The_Category_Route\" title=\"The Category Route\">The Category Route<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/#The_Category_Link\" title=\"The Category Link\">The Category Link<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/#Category_Controller_Creation\" title=\"Category Controller Creation\">Category Controller Creation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-7-playing-with-the-category-page\/#The_result\" title=\"The result:\">The result:<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"_This_article_is_part_of_the_original_Jobeet_Tutorial_created_by_Fabien_Potencier_for_Symfony_14_Category_page_in_jobeet\"><\/span><span style=\"font-family: timesnew roman; font-size: 12px;\">* This article is part of the original <a href=\"http:\/\/symfony.com\/legacy\/doc\/jobeet?orm=Doctrine\" target=\"_blank\" rel=\"noopener\">Jobeet Tutorial<\/a>, created by Fabien Potencier, for Symfony 1.4.<\/span><br \/>\nCategory page in jobeet<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Today we will make the Category page like it is described in the second day\u2019s requirements:<\/p>\n<blockquote><p>\u201cThe user sees a list of all the jobs from the category sorted by date and paginated with 20 jobs per page\u201c<span id=\"more-185\"><\/span><\/p><\/blockquote>\n<h2><span class=\"ez-toc-section\" id=\"The_Category_Route\"><\/span>The Category Route<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>First, we need to add a route to define a pretty URL for the category page. Add it at the beginning of the <code>routing<\/code> file:<\/p>\n<pre class=\"lang:yaml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/config\/routing.yml\"># ...\r\nIbwJobeetBundle_category:\r\n    pattern:  \/category\/{slug}\r\n    defaults: { _controller: IbwJobeetBundle:Category:show }<\/pre>\n<p>To get the slug of a category we need to add the <code>getSlug()<\/code> method to our <code>category<\/code> class:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Entity\/Category.php\">use IbwJobeetBundleUtilsJobeet as Jobeet;\r\n\r\nclass Category\r\n{\r\n    \/\/ ...\r\n\r\n    public function getSlug()\r\n    {\r\n        return Jobeet::slugify($this-&gt;getName());\r\n    }\r\n}<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"The_Category_Link\"><\/span>The Category Link<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Now, edit the <code>index.html.twig<\/code> template of the <code>job<\/code> controller to add the link to the category page:<\/p>\n<pre class=\"lang:xhtml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/views\/Job\/index.html.twig\">&lt;!-- some HTML code --&gt;\r\n\r\n                    &lt;h1&gt;&lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug }) }}\"&gt;{{ category.name }}&lt;\/a&gt;&lt;\/h1&gt;\r\n\r\n&lt;!-- some HTML code --&gt;\r\n\r\n                &lt;\/table&gt;\r\n\r\n                {% if category.morejobs %}\r\n                    &lt;div class=\"more_jobs\"&gt;\r\n                        and &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug }) }}\"&gt;{{ category.morejobs }}&lt;\/a&gt;\r\n                        more...\r\n                    &lt;\/div&gt;\r\n                {% endif %}\r\n            &lt;\/div&gt;\r\n        {% endfor %}\r\n    &lt;\/div&gt;\r\n{% endblock %}<\/pre>\n<p>In the template above we used <code>category.morejobs<\/code>, so let\u2019s define it:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBunlde\/Entity\/Category.php\">class Category\r\n{\r\n    \/\/ ...\r\n\r\n    private $more_jobs;\r\n\r\n    \/\/ ...\r\n\r\n    public function setMoreJobs($jobs)\r\n    {\r\n        $this-&amp;gt;more_jobs = $jobs &amp;gt;=  0 ? $jobs : 0;\r\n    }\r\n\r\n    public function getMoreJobs()\r\n    {\r\n        return $this-&amp;gt;more_jobs;\r\n    }\r\n}<\/pre>\n<p>The <code>more_jobs<\/code> property will hold the number of active jobs for the category minus the number of jobs listed on the homepage. Now, in <code>JobController<\/code>, we need to set the <code>more_jobs <\/code>value for each category:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Controller\/JobController.php\">public function indexAction()\r\n{\r\n    $em = $this-&amp;gt;getDoctrine()-&amp;gt;getManager();\r\n\r\n    $categories = $em-&amp;gt;getRepository('IbwJobeetBundle:Category')-&amp;gt;getWithJobs();\r\n\r\n    foreach($categories as $category)\r\n    {\r\n        $category-&amp;gt;setActiveJobs($em-&amp;gt;getRepository('IbwJobeetBundle:Job')-&amp;gt;getActiveJobs($category-&amp;gt;getId(), $this-&amp;gt;container-&amp;gt;getParameter('max_jobs_on_homepage')));\r\n        $category-&amp;gt;setMoreJobs($em-&amp;gt;getRepository('IbwJobeetBundle:Job')-&amp;gt;countActiveJobs($category-&amp;gt;getId()) - $this-&amp;gt;container-&amp;gt;getParameter('max_jobs_on_homepage'));\r\n    }\r\n\r\n    return $this-&amp;gt;render('IbwJobeetBundle:Job:index.html.twig', array(\r\n        'categories' =&amp;gt; $categories\r\n    ));\r\n}<\/pre>\n<p>The <code>countActiveJobs<\/code> function has to be added to the <code>JobRepository<\/code>:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Repository\/JobRepository.php\">\/\/ ...\r\n\r\npublic function countActiveJobs($category_id = null)\r\n{\r\n    $qb = $this-&amp;gt;createQueryBuilder('j')\r\n        -&amp;gt;select('count(j.id)')\r\n        -&amp;gt;where('j.expires_at &amp;gt; :date')\r\n        -&amp;gt;setParameter('date', date('Y-m-d H:i:s', time()));\r\n\r\n    if($category_id)\r\n    {\r\n        $qb-&amp;gt;andWhere('j.category = :category_id')\r\n        -&amp;gt;setParameter('category_id', $category_id);\r\n    }\r\n\r\n    $query = $qb-&amp;gt;getQuery();\r\n\r\n    return $query-&amp;gt;getSingleScalarResult();\r\n}\r\n\r\n\/\/ ...<\/pre>\n<p>Now you should see the result in your browser:<\/p>\n<p><a href=\"\/\/intelligentbee.com\/blog\/wp-content\/uploads\/2017\/03\/Day-7-category-link.png\"><img decoding=\"async\" class=\"alignnone wp-image-55 size-full\" src=\"\/\/intelligentbee.com\/blog\/wp-content\/uploads\/2017\/03\/Day-7-category-link.png\" alt=\"category page in jobeet\" width=\"1917\" height=\"1040\" \/><\/a><\/p>\n<h2><span class=\"ez-toc-section\" id=\"Category_Controller_Creation\"><\/span>Category Controller Creation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It\u2019s now time to create the <code>Category<\/code> controller. Create a new <code>CategoryController.php<\/code> file in your <code>Controller<\/code> directory:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Controller\/CategoryController.php\">namespace IbwJobeetBundleController;\r\n\r\nuse SymfonyBundleFrameworkBundleControllerController;\r\nuse IbwJobeetBundleEntityCategory;\r\n\r\n\/**\r\n* Category controller\r\n*\r\n*\/\r\nclass CategoryController extends Controller\r\n{\r\n\r\n}<\/pre>\n<p>We could use the <code>doctrine:generate:crud<\/code> command like we did for the <code>job <\/code>controller, but we won\u2019t need 90% of the generated code, so we can just create a new controller from scratch.<\/p>\n<h3>Update the Database<\/h3>\n<p>We need to add a <code>slug<\/code> column for the category table and lifecycle callbacks for setting this column value:<\/p>\n<pre class=\"lang:yaml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/config\/doctrine\/Category.orm.yml\">IbwJobeetBundleEntityCategory:\r\n    type: entity\r\n    repositoryClass: IbwJobeetBundleRepositoryCategoryRepository\r\n    table: category\r\n    id:\r\n        id:\r\n            type: integer\r\n            generator: { strategy: AUTO }\r\n    fields:\r\n        name:\r\n            type: string\r\n            length: 255\r\n            unique: true\r\n        slug:\r\n            type: string\r\n            length: 255\r\n            unique: true\r\n    oneToMany:\r\n        jobs:\r\n            targetEntity: Job\r\n            mappedBy: category\r\n    manyToMany:\r\n        affiliates:\r\n            targetEntity: Affiliate\r\n            mappedBy: categories\r\n    lifecycleCallbacks:\r\n        prePersist: [ setSlugValue ]\r\n        preUpdate: [ setSlugValue ]<\/pre>\n<p>Remove from the <code>Category<\/code> entity <code>(src\/Ibw\/JobeetBundle\/Entity\/Category.php)<\/code> the <code>getSlug <\/code>method we created earlier and run the doctrine command to update the <code>Category<\/code> entity class:<\/p>\n<pre class=\"toolbar:2 nums:false lang:default decode:true \">php app\/console doctrine:generate:entities<\/pre>\n<p>Now you should have the following added to <code>Category.php<\/code>:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Entity\/Category.php\">\/\/ ...    \r\n    \/**\r\n     * @var string\r\n     *\/\r\n    private $slug;\r\n\r\n    \/**\r\n     * Set slug\r\n     *\r\n     * @param string $slug\r\n     * @return Category\r\n     *\/\r\n    public function setSlug($slug)\r\n    {\r\n        $this-&amp;gt;slug = $slug;\r\n\r\n        return $this;\r\n    }\r\n\r\n    \/**\r\n     * Get slug\r\n     *\r\n     * @return string \r\n     *\/\r\n    public function getSlug()\r\n    {\r\n        return $this-&amp;gt;slug;\r\n    }<\/pre>\n<p>Change the s<code>etSlugValue()<\/code> function:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Entity\/Category.php\">\/\/ ...\r\n\r\nclass Category\r\n{\r\n    \/\/ ...\r\n\r\n    public function setSlugValue()\r\n    {\r\n        $this-&amp;gt;slug = Jobeet::slugify($this-&amp;gt;getName());\r\n    }\r\n}<\/pre>\n<p>Now we have to drop the database and create it again with the new <code>Category<\/code> column and load the <code>fixtures<\/code>:<\/p>\n<pre class=\"toolbar:2 nums:false lang:default decode:true \">php app\/console doctrine:database:drop --force\r\nphp app\/console doctrine:database:create \r\nphp app\/console doctrine:schema:update --force\r\nphp app\/console doctrine:fixtures:load<\/pre>\n<h3>Category Page<\/h3>\n<p>We have now everything in place to create the <code>showAction()<\/code> method. Add the following code to the <code>CategoryController.php<\/code> file:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Controller\/CategoryController.php\">\/\/ ...\r\n\r\npublic function showAction($slug)\r\n{\r\n    $em = $this-&amp;gt;getDoctrine()-&amp;gt;getManager();\r\n\r\n    $category = $em-&amp;gt;getRepository('IbwJobeetBundle:Category')-&amp;gt;findOneBySlug($slug);\r\n\r\n    if (!$category) {\r\n        throw $this-&amp;gt;createNotFoundException('Unable to find Category entity.');\r\n    }\r\n\r\n    $category-&amp;gt;setActiveJobs($em-&amp;gt;getRepository('IbwJobeetBundle:Job')-&amp;gt;getActiveJobs($category-&amp;gt;getId()));\r\n\r\n    return $this-&amp;gt;render('IbwJobeetBundle:Category:show.html.twig', array(\r\n        'category' =&amp;gt; $category,\r\n    ));\r\n}\r\n\r\n\/\/ ...<\/pre>\n<p>The last step is to create the <code>show.html.twig<\/code> template:<\/p>\n<pre class=\"lang:xhtml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/views\/Category\/show.html.twig\">{% extends 'IbwJobeetBundle::layout.html.twig' %}\r\n\r\n{% block title %}\r\n    Jobs in the {{ category.name }} category\r\n{% endblock %}\r\n\r\n{% block stylesheets %}\r\n    {{ parent() }}\r\n    &lt;link rel=\"stylesheet\" href=\"{{ asset('bundles\/ibwjobeet\/css\/jobs.css') }}\" type=\"text\/css\" media=\"all\" \/&gt;\r\n{% endblock %}\r\n\r\n{% block content %}\r\n    &lt;div class=\"category\"&gt;\r\n        &lt;div class=\"feed\"&gt;\r\n            &lt;a href=\"\"&gt;Feed&lt;\/a&gt;\r\n        &lt;\/div&gt;\r\n        &lt;h1&gt;{{ category.name }}&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;table class=\"jobs\"&gt;\r\n        {% for entity in category.activejobs %}\r\n            &lt;tr class=\"{{ cycle(['even', 'odd'], loop.index) }}\"&gt;\r\n                &lt;td class=\"location\"&gt;{{ entity.location }}&lt;\/td&gt;\r\n                &lt;td class=\"position\"&gt;\r\n                    &lt;a href=\"{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}\"&gt;\r\n                        {{ entity.position }}\r\n                    &lt;\/a&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td class=\"company\"&gt;{{ entity.company }}&lt;\/td&gt;\r\n            &lt;\/tr&gt;\r\n        {% endfor %}\r\n    &lt;\/table&gt;\r\n{% endblock %}<\/pre>\n<h3><strong>Including Other Twig Templates<\/strong><\/h3>\n<p>Notice that we have copied and pasted the <code><\/code>tag that create a list of jobs from the job <code>index.html.twig<\/code> template. That\u2019s bad. When you need to reuse some portion of a template, you need to create a new twig template with that code and include it where you need. Create the <code>list.html.twig<\/code> file:<\/p>\n<pre class=\"lang:xhtml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/views\/Job\/list.html.twig\">&lt;table class=\"jobs\"&gt;\r\n    {% for entity in jobs %}\r\n        &lt;tr class=\"{{ cycle(['even', 'odd'], loop.index) }}\"&gt;\r\n            &lt;td class=\"location\"&gt;{{ entity.location }}&lt;\/td&gt;\r\n            &lt;td class=\"position\"&gt;\r\n                &lt;a href=\"{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}\"&gt;\r\n                    {{ entity.position }}\r\n                &lt;\/a&gt;\r\n            &lt;\/td&gt;\r\n            &lt;td class=\"company\"&gt;{{ entity.company }}&lt;\/td&gt;\r\n        &lt;\/tr&gt;\r\n    {% endfor %}\r\n&lt;\/table&gt;<\/pre>\n<p>You can include a template by using the <code> include<\/code> function. Replace the <code><\/code>HTML <code>&lt;table&gt; <\/code>code from both templates with the mentioned function:<\/p>\n<pre class=\"lang:xhtml decode:true\" title=\"src\/Ibw\/JobeetBundle\/Resources\/view\/Job\/index.html.twig\">{{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}<\/pre>\n<pre class=\"lang:xhtml decode:true\" title=\"src\/Ibw\/JobeetBundle\/Resources\/view\/Category\/show.html.twig\">{{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}<\/pre>\n<h3>List Pagination<\/h3>\n<p>At the moment of writing this, Symfony2 doesn\u2019t provide any good pagination tools out of the box so to solve this problem we will use the old classic method. First, let\u2019s add a page parameter to the <code>IbwJobeetBundle_category<\/code> route. The page parameter will have a default value of 1, so it will not be required:<\/p>\n<pre class=\"lang:yaml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/config\/routing.yml\">IbwJobeetBundle_category:\r\n    pattern: \/category\/{slug}\/{page}\r\n    defaults: { _controller: IbwJobeetBundle:Category:show, page: 1 }\r\n\r\n# ...<\/pre>\n<p>Clear the cache after modifying the <code>routing<\/code> file:<\/p>\n<pre class=\"toolbar:2 nums:false lang:default decode:true \">php app\/console cache:clear --env=dev\r\nphp app\/console cache:clear --env=prod<\/pre>\n<p>The number of jobs on each page will be defined as a custom parameter in the <code>app\/config\/config.yml<\/code> file:<\/p>\n<pre class=\"lang:yaml decode:true \" title=\"app\/config\/config.yml\"># ...\r\n\r\nparameters:\r\n    max_jobs_on_homepage: 10\r\n    max_jobs_on_category: 20<\/pre>\n<p>Change the <code>JobRepository getActiveJobs<\/code> method to include an <code>$offset<\/code> parameter to be used by doctrine when retrieving jobs:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Repository\/JobRepository.php\">\/\/ ...\r\n\r\npublic function getActiveJobs($category_id = null, $max = null, $offset = null)\r\n{\r\n    $qb = $this-&amp;gt;createQueryBuilder('j')\r\n        -&amp;gt;where('j.expires_at &amp;gt; :date')\r\n        -&amp;gt;setParameter('date', date('Y-m-d H:i:s', time()))\r\n        -&amp;gt;orderBy('j.expires_at', 'DESC');\r\n\r\n    if($max)\r\n    {\r\n        $qb-&amp;gt;setMaxResults($max);\r\n    }\r\n\r\n    if($offset)\r\n    {\r\n        $qb-&amp;gt;setFirstResult($offset);\r\n    }\r\n\r\n    if($category_id)\r\n    {\r\n        $qb-&amp;gt;andWhere('j.category = :category_id')\r\n           -&amp;gt;setParameter('category_id', $category_id);\r\n    }\r\n\r\n    $query = $qb-&amp;gt;getQuery();\r\n\r\n    return $query-&amp;gt;getResult();\r\n}\r\n\r\n\/\/<\/pre>\n<p>Change the <code>CategoryController<\/code> <code>showAction<\/code> to the following:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Controller\/CategoryController.php\">public function showAction($slug, $page)\r\n{\r\n    $em = $this-&amp;gt;getDoctrine()-&amp;gt;getManager();\r\n\r\n    $category = $em-&amp;gt;getRepository('IbwJobeetBundle:Category')-&amp;gt;findOneBySlug($slug);\r\n\r\n    if (!$category) {\r\n        throw $this-&amp;gt;createNotFoundException('Unable to find Category entity.');\r\n    }\r\n\r\n    $total_jobs = $em-&amp;gt;getRepository('IbwJobeetBundle:Job')-&amp;gt;countActiveJobs($category-&amp;gt;getId());\r\n    $jobs_per_page = $this-&amp;gt;container-&amp;gt;getParameter('max_jobs_on_category');\r\n    $last_page = ceil($total_jobs \/ $jobs_per_page);\r\n    $previous_page = $page &amp;gt; 1 ? $page - 1 : 1;\r\n    $next_page = $page &amp;lt; $last_page ? $page + 1 : $last_page;\r\n    $category-&amp;gt;setActiveJobs($em-&amp;gt;getRepository('IbwJobeetBundle:Job')-&amp;gt;getActiveJobs($category-&amp;gt;getId(), $jobs_per_page, ($page - 1) * $jobs_per_page));\r\n\r\n    return $this-&amp;gt;render('IbwJobeetBundle:Category:show.html.twig', array(\r\n        'category' =&amp;gt; $category,\r\n        'last_page' =&amp;gt; $last_page,\r\n        'previous_page' =&amp;gt; $previous_page,\r\n        'current_page' =&amp;gt; $page,\r\n        'next_page' =&amp;gt; $next_page,\r\n        'total_jobs' =&amp;gt; $total_jobs\r\n    ));\r\n}<\/pre>\n<h3>Finally, let\u2019s update the template<\/h3>\n<pre class=\"lang:xhtml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/views\/Category\/show.html.twig\">{% extends 'IbwJobeetBundle::layout.html.twig' %}\r\n\r\n{% block title %}\r\n    Jobs in the {{ category.name }} category\r\n{% endblock %}\r\n\r\n{% block stylesheets %}\r\n    {{ parent() }}\r\n    &lt;link rel=\"stylesheet\" href=\"{{ asset('bundles\/ibwjobeet\/css\/jobs.css') }}\" type=\"text\/css\" media=\"all\" \/&gt;\r\n{% endblock %}\r\n\r\n{% block content %}\r\n    &lt;div class=\"category\"&gt;\r\n        &lt;div class=\"feed\"&gt;\r\n            &lt;a href=\"\"&gt;Feed\r\n            &lt;\/a&gt;\r\n        &lt;\/div&gt;\r\n        &lt;h1&gt;{{ category.name }}&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n\r\n    {{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}\r\n\r\n    {% if last_page &gt; 1 %}\r\n        &lt;div class=\"pagination\"&gt;\r\n            &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': 1 }) }}\"&gt;\r\n                &lt;img src=\"{{ asset('bundles\/ibwjobeet\/images\/first.png') }}\" alt=\"First page\" title=\"First page\" \/&gt;\r\n            &lt;\/a&gt;\r\n\r\n            &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': previous_page }) }}\"&gt;\r\n                &lt;img src=\"{{ asset('bundles\/ibwjobeet\/images\/previous.png') }}\" alt=\"Previous page\" title=\"Previous page\" \/&gt;\r\n            &lt;\/a&gt;\r\n\r\n            {% for page in 1..last_page %}\r\n                {% if page == current_page %}\r\n                    {{ page }}\r\n                {% else %}\r\n                    &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': page }) }}\"&gt;{{ page }}&lt;\/a&gt;\r\n                {% endif %}\r\n            {% endfor %}\r\n\r\n            &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': next_page }) }}\"&gt;\r\n                &lt;img src=\"{{ asset('bundles\/ibwjobeet\/images\/next.png') }}\" alt=\"Next page\" title=\"Next page\" \/&gt;\r\n            &lt;\/a&gt;\r\n\r\n            &lt;a href=\"{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': last_page }) }}\"&gt;\r\n                &lt;img src=\"{{ asset('bundles\/ibwjobeet\/images\/last.png') }}\" alt=\"Last page\" title=\"Last page\" \/&gt;\r\n            &lt;\/a&gt;\r\n        &lt;\/div&gt;\r\n    {% endif %}\r\n\r\n    &lt;div class=\"pagination_desc\"&gt;\r\n        &lt;strong&gt;{{ total_jobs }}&lt;\/strong&gt; jobs in this category\r\n\r\n        {% if last_page &gt; 1 %}\r\n            - page &lt;strong&gt;{{ current_page }}\/{{ last_page }}&lt;\/strong&gt;\r\n        {% endif %}\r\n    &lt;\/div&gt;\r\n{% endblock %}<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"The_result\"><\/span>The result:<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><a href=\"\/\/intelligentbee.com\/blog\/wp-content\/uploads\/2017\/03\/Day-7-pagination.png\"><img decoding=\"async\" class=\"alignnone wp-image-56 size-full\" src=\"\/\/intelligentbee.com\/blog\/wp-content\/uploads\/2017\/03\/Day-7-pagination.png\" alt=\"category page in jobeet\" width=\"1767\" height=\"965\" \/><\/a><br \/>\n<a href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\" rel=\"license\"><img decoding=\"async\" style=\"border-width: 0;\" src=\"\/\/i.creativecommons.org\/l\/by-sa\/3.0\/88x31.png\" alt=\"Creative Commons License\" \/><\/a><br \/>\nThis work is licensed under a <a href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\" target=\"_blank\" rel=\"license noopener\">Creative Commons Attribution-ShareAlike 3.0 Unported License<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>* This article is part of the original Jobeet Tutorial, created by Fabien Potencier, for Symfony 1.4. Category page in [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[82],"tags":[],"yst_prominent_words":[6,297,394,476,798,1013,1038,1133,1356,1395,1556,2117,2509],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2212"}],"collection":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/comments?post=2212"}],"version-history":[{"count":4,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2212\/revisions"}],"predecessor-version":[{"id":133242,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2212\/revisions\/133242"}],"wp:attachment":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/media?parent=2212"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/categories?post=2212"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/tags?post=2212"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/yst_prominent_words?post=2212"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}