Wednesday, February 18, 2015

Symfony 2 Hari 8: Bermain dengan halaman kategori

Artikel ini dirunut berdasarkan Jobeet Tutorial, yang dibuat oleh Fabien Potencier, untuk Symfony 1.4.

Routing kategorinya

Hal pertama yang akan kita lakukan adalah membuat url untuk halaman kategori agar lebih mudah dibaca. Untuk itu kita harus mengubah konfigurasi routingnya. Edit file konfigurasi routing, dengan menambahkan kode berikut:

src/Ibw/JobeetBundle/Resources/config/routing.yml

# ...
IbwJobeetBundle_category:
    pattern:  /category/{slug}


Untuk dapat menggunakan method getSlug() kita harus menambahkannya di model Category

src/Ibw/JobeetBundle/Entity/Category.php

use Ibw\JobeetBundle\Utils\Jobeet as Jobeet;
class Category
{
    // ...
    public function getSlug()
    {
        return Jobeet::slugify($this->getName());
    }
}

Link kategorinya

Sekarang kita harus mengedit index.html.twig, untuk menambahkan link ke halaman kategori.

src/Ibw/JobeetBundle/Resources/views/Job/index.html.twig

<!-- some HTML code -->
                    <h1><a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug }) }}">{{ category.name }}</a></h1>
<!-- some HTML code -->
                </table>
                {% if category.morejobs %}
                    <div class="more_jobs">
                        and <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug }) }}">{{ category.morejobs }}</a>
                        more...
                    </div>
                {% endif %}
            </div>
        {% endfor %}
    </div>
{% endblock %}

di template tersebut kita menggunakan category.morejobs, untuk itu mari kita definisikan di model Category:

src/Ibw/JobeetBunlde/Entity/Category.php

class Category
{
    // ...
    private $more_jobs;
    // ...
    public function setMoreJobs($jobs)
    {
        $this->more_jobs = $jobs >=  0 ? $jobs : 0;
    }
    public function getMoreJobs()
    {
        return $this->more_jobs;
    }
}


Attribut more_job akan menampilkan jumlah lowongan dari halaman kategori selain lowongan yang sudah ditampilkan sebelumnya. Sekarang di JobController kita harus menambahkan value dari more_jobs untuk masing-masing kategori.


src/Ibw/JobeetBundle/Controller/JobController.php

public function indexAction()
{
    $em = $this->getDoctrine()->getManager();
    $categories = $em->getRepository('IbwJobeetBundle:Category')->getWithJobs();
    foreach($categories as $category)
    {
        $category->setActiveJobs($em->getRepository('IbwJobeetBundle:Job')->getActiveJobs($category->getId(), $this->container->getParameter('max_jobs_on_homepage')));
        $category->setMoreJobs($em->getRepository('IbwJobeetBundle:Job')->countActiveJobs($category->getId()) - $this->container->getParameter('max_jobs_on_homepage'));
    }
    return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
        'categories' => $categories
    ));
}
Dan kita juga harus menambahkan fungsi countActiveJobs di repositori kita

src/Ibw/JobeetBundle/Repository/JobRepository.php

// ...
public function countActiveJobs($category_id = null)
{
    $qb = $this->createQueryBuilder('j')
        ->select('count(j.id)')
        ->where('j.expires_at > :date')
        ->setParameter('date', date('Y-m-d H:i:s', time()));
    if($category_id)
    {
        $qb->andWhere('j.category = :category_id')
        ->setParameter('category_id', $category_id);
    }
    $query = $qb->getQuery();
    return $query->getSingleScalarResult();
}
// ...

Modifikasi Category Controller

Sekarang saatnya membuat CategoryController, buat file baru bernama CategoryController.php di dalam folder Controller.

src/Ibw/JobeetBundle/Controller/CategoryController.php

namespace Ibw\JobeetBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Ibw\JobeetBundle\Entity\Category;
/**
* Category controller
*
*/
class CategoryController extends Controller
{
}
 

Update databasenya


Kita perlu menambahkan field slug di tabel kategori, untuk itu tambahkan filed baru di file Category.orm.yml 


src/Ibw/JobeetBundle/Resources/config/doctrine/Category.orm.yml

Ibw\JobeetBundle\Entity\Category:
    type: entity
    repositoryClass: Ibw\JobeetBundle\Repository\CategoryRepository
    table: category
    id:
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        name:
            type: string
            length: 255
            unique: true
        slug:
            type: string
            length: 255
            unique: true
    oneToMany:
        jobs:
            targetEntity: Job
            mappedBy: category
    manyToMany:
        affiliates:
            targetEntity: Affiliate
            mappedBy: categories
    lifecycleCallbacks:
        prePersist: [ setSlugValue ]
        preUpdate: [ setSlugValue ]


Hapus method getSlug() dari model Category (src/Ibw/JobeetBundle/Entity/Category.php). Kemudian update databasenya dengan menjalankan perintah berikut:

php app/console doctrine:generate:entities

Jika kamu buka file model Category maka kamu akan melihat kode berikut:

src/Ibw/JobeetBundle/Entity/Category.php

// ...    
    /**
     * @var string
     */
    private $slug;
    /**
     * Set slug
     *
     * @param string $slug
     * @return Category
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;
        return $this;
    }
    /**
     * Get slug
     *
     * @return string
     */
    public function getSlug()
    {
        return $this->slug;
    }

Sesuaikan method SetSlugValue()


src/Ibw/JobeetBundle/Entity/Category.php


// ...
class Category
{
    // ...
    public function setSlugValue()
    {
        $this->slug = Jobeet::slugify($this->getName());
    }
}

Sekarang kita drop databasenya dan kita buat ulang serta kita reload fixturenya dengan menggunakan perintah berikut:

php app/console doctrine:database:drop --force
php app/console doctrine:database:create
php app/console doctrine:schema:update --force
php app/console doctrine:fixtures:load

Halaman kategorinya

Sekarang kita perlu mengedit Category Controllernya, pada method showAction() tambahkan kode berikut:

src/Ibw/JobeetBundle/Controller/CategoryController.php

// ...
public function showAction($slug)
{
    $em = $this->getDoctrine()->getManager();
    $category = $em->getRepository('IbwJobeetBundle:Category')->findOneBySlug($slug);
    if (!$category) {
        throw $this->createNotFoundException('Unable to find Category entity.');
    }
    $category->setActiveJobs($em->getRepository('IbwJobeetBundle:Job')->getActiveJobs($category->getId()));
    return $this->render('IbwJobeetBundle:Category:show.html.twig', array(
        'category' => $category,
    ));
}
// ...

Langkah terakhir adalah membuat file show.html.twig untuk menampilkan halaman kategori.

src/Ibw/JobeetBundle/Resources/views/Category/show.html.twig

{% extends 'IbwJobeetBundle::layout.html.twig' %}
{% block title %}
    Jobs in the {{ category.name }} category
{% endblock %}
{% block stylesheets %}
    {{ parent() }}
    <link rel="stylesheet" href="{{ asset('bundles/ibwjobeet/css/jobs.css') }}" type="text/css" media="all" />
{% endblock %}
{% block content %}
    <div class="category">
        <div class="feed">
            <a href="">Feed</a>
        </div>
        <h1>{{ category.name }}</h1>
    </div>
    <table class="jobs">
        {% for entity in category.activejobs %}
            <tr class="{{ cycle(['even', 'odd'], loop.index) }}">
                <td class="location">{{ entity.location }}</td>
                <td class="position">
                    <a href="{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}">
                        {{ entity.position }}
                    </a>
                </td>
                <td class="company">{{ entity.company }}</td>
            </tr>
        {% endfor %}
    </table>
{% endblock %}

Mengikludkan template twig lainnya

Buat file list.html.twig untuk menampilkan list dari job di halaman kategori.

src/Ibw/JobeetBundle/Resources/views/Job/list.html.twig

<table class="jobs">
    {% for entity in jobs %}
        <tr class="{{ cycle(['even', 'odd'], loop.index) }}">
            <td class="location">{{ entity.location }}</td>
            <td class="position">
                <a href="{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug }) }}">
                    {{ entity.position }}
                </a>
            </td>
            <td class="company">{{ entity.company }}</td>
        </tr>
    {% endfor %}
</table>

Kamu bisa mengincludekan file template denga menggunakan fungsi include. Replace tag <table> .... </table> pada file berikut:

src/Ibw/JobeetBundle/Resources/view/Job/index.html.twig
 
{{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}
src/Ibw/JobeetBundle/Resources/view/Category/show.html.twig

{{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}

Membuat pagination

Langkah awal untuk membuat pagination adalah dengan membuat routingnya terlebih dahulu.

src/Ibw/JobeetBundle/Resources/config/routing.yml

IbwJobeetBundle_category:
    pattern: /category/{slug}/{page}
    defaults: { _controller: IbwJobeetBundle:Category:show, page: 1 }
# ...

Kemudian bersihkan chace setelah memodifikasi file routing:


php app/console cache:clear --env=dev
php app/console cache:clear --env=prod

Set parameter value untuk membatasi data Job pada halaman Category.

app/config/config.yml


// ...
public function getActiveJobs($category_id = null, $max = null, $offset = null)
{
    $qb = $this->createQueryBuilder('j')
        ->where('j.expires_at > :date')
        ->setParameter('date', date('Y-m-d H:i:s', time()))
        ->orderBy('j.expires_at', 'DESC');
    if($max)
    {
        $qb->setMaxResults($max);
    }
    if($offset)
    {
        $qb->setFirstResult($offset);
    }
    if($category_id)
    {
        $qb->andWhere('j.category = :category_id')
           ->setParameter('category_id', $category_id);
    }
    $query = $qb->getQuery();
    return $query->getResult();
}

Ubah lagi method showAction() pada CategoryController, seperti berikut ini:

src/Ibw/JobeetBundle/Controller/CategoryController.php

public function showAction($slug, $page)
{
    $em = $this->getDoctrine()->getManager();
    $category = $em->getRepository('IbwJobeetBundle:Category')->findOneBySlug($slug);
    if (!$category) {
        throw $this->createNotFoundException('Unable to find Category entity.');
    }
    $total_jobs = $em->getRepository('IbwJobeetBundle:Job')->countActiveJobs($category->getId());
    $jobs_per_page = $this->container->getParameter('max_jobs_on_category');
    $last_page = ceil($total_jobs / $jobs_per_page);
    $previous_page = $page > 1 ? $page - 1 : 1;
    $next_page = $page < $last_page ? $page + 1 : $last_page;
    $category->setActiveJobs($em->getRepository('IbwJobeetBundle:Job')->getActiveJobs($category->getId(), $jobs_per_page, ($page - 1) * $jobs_per_page));
    return $this->render('IbwJobeetBundle:Category:show.html.twig', array(
        'category' => $category,
        'last_page' => $last_page,
        'previous_page' => $previous_page,
        'current_page' => $page,
        'next_page' => $next_page,
        'total_jobs' => $total_jobs
    ));
}


Terakhir update templatenya

src/Ibw/JobeetBundle/Resources/views/Category/show.html.twig

{% extends 'IbwJobeetBundle::layout.html.twig' %}
{% block title %}
    Jobs in the {{ category.name }} category
{% endblock %}
{% block stylesheets %}
    {{ parent() }}
    <link rel="stylesheet" href="{{ asset('bundles/ibwjobeet/css/jobs.css') }}" type="text/css" media="all" />
{% endblock %}
{% block content %}
    <div class="category">
        <div class="feed">
            <a href="">Feed
            </a>
        </div>
        <h1>{{ category.name }}</h1>
    </div>
    {{ include ('IbwJobeetBundle:Job:list.html.twig', {'jobs': category.activejobs}) }}
    {% if last_page > 1 %}
        <div class="pagination">
            <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': 1 }) }}">
                <img src="{{ asset('bundles/ibwjobeet/images/first.png') }}" alt="First page" title="First page" />
            </a>
            <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': previous_page }) }}">
                <img src="{{ asset('bundles/ibwjobeet/images/previous.png') }}" alt="Previous page" title="Previous page" />
            </a>
            {% for page in 1..last_page %}
                {% if page == current_page %}
                    {{ page }}
                {% else %}
                    <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': page }) }}">{{ page }}</a>
                {% endif %}
            {% endfor %}
            <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': next_page }) }}">
                <img src="{{ asset('bundles/ibwjobeet/images/next.png') }}" alt="Next page" title="Next page" />
            </a>
            <a href="{{ path('IbwJobeetBundle_category', { 'slug': category.slug, 'page': last_page }) }}">
                <img src="{{ asset('bundles/ibwjobeet/images/last.png') }}" alt="Last page" title="Last page" />
            </a>
        </div>
    {% endif %}
    <div class="pagination_desc">
        <strong>{{ total_jobs }}</strong> jobs in this category
        {% if last_page > 1 %}
            - page <strong>{{ current_page }}/{{ last_page }}</strong>
        {% endif %}
    </div>
{% endblock %}

Mmmm... ada masalah? yap semoga tidak kita lanjut lagi besok bye!

1 comment:

  1. Mantap mas. tutorial nya mudah di pahami untuk pemula symfony sebagi best pratice.
    lanjutkan :D

    ReplyDelete