Artikel ini dirunut berdasarkan Jobeet Tutorial, yang dibuat oleh Fabien Potencier, untuk Symfony 1.4.
Doctrine query object
Untuk saat ini semua lowongan akan ditampilkan meski lowongan tersebut tidak aktif. Kode saat ini:src/Ibw/JobeetBundle/Controller/JobController.php
// ...
class JobController extends Controller
{
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('IbwJobeetBundle:Job')->findAll();
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'entities' => $entities
));
// ...
}
Job yang aktif adalah job yang umur postingannya kurang dari 30 hari. Kita menggunakan fungsi:
$entities = $em->getRepository('IbwJobeetBundle:Job')->findAll();
yang akan memanggil semua data dari database tanpa mengecek apakah data job tersebut masih aktif atau tidak. Mari kita ubah agar hanya menampilkan job yang masih aktif saja:
src/Ibw/JobeetBundle/Controller/JobController.php
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT j FROM IbwJobeetBundle:Job j WHERE j.created_at > :date'
)->setParameter('date', date('Y-m-d H:i:s', time() - 86400 * 30));
$entities = $query->getResult();
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'entities' => $entities
));
}
Debugging doctrine genrated SQL
Terkadang query yang kita buat tidak berjalan sesuai dengan harpan, untuk itu berterima kasihlah kepada Symfony web debug toolbar yang muncul jika kita berada dalam dev mode (localhost/sandbox/web/app_dev.php)Serialisasi object
Orang yang memposting job dapat memperpanjang masa aktif lowongan tersebut, jika kamu ingat kemarin kita sudah membuat satu field yaitu expires_at yang untuk saat ini masih kosong. Untuk itu kita harus menambahkan lifecycleCallback method agar field expires_at dapat terisi secara otomatis saat ada data baru yang masuk.src/Ibw/JobeetBundle/Resources/config/doctrine/Job.orml.yml
# ...
# ...
lifecycleCallbacks:
prePersist: [ setCreatedAtValue, setExpiresAtValue ]
preUpdate: [ setUpdatedAtValue ]
Setelah itu kita build ulang entity kita agar object modelnya terupdate.
php app/console doctrine:generate:entities IbwJobeetBundle
Buka file src/Ibw/JobeetBundle/Entity/Job.php, lalu edit fungsinya
src/Ibw/JobeetBundle/Entity/Job.php
// ...
class Job
{
// ...
public function setExpiresAtValue()
{
if(!$this->getExpiresAt()) {
$now = $this->getCreatedAt() ? $this->getCreatedAt()->format('U') : time();
$this->expires_at = new \DateTime(date('Y-m-d H:i:s', $now + 86400 * 30));
}
}
}
Sekarang kita edit lagi JobControllernya untuk menambahkan expires_at parameter.
src/Ibw/JobeetBundle/Controller/JobController.php
// ...
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT j FROM IbwJobeetBundle:Job j WHERE j.expires_at > :date'
)->setParameter('date', date('Y-m-d H:i:s', time()));
$entities = $query->getResult();
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'entities' => $entities
));
}
// ...
Lebih jauh lagi mengenai fixtures
Sekarang kalau kamu merefresh halaman utamanya kamu tidak akan melihat apapun karena data lama kita field expires_at-nya masih kosong. Agar lebih beragam kita tambahkan juga sample data yang sudah tidak aktif (usianya lebih dari 30 hari).src/Ibw/JobeetBundle/DataFixtures/ORM/LoadJobData.php
// ...
public function load(ObjectManager $em)
{
$job_expired = new Job();
$job_expired->setCategory($em->merge($this->getReference('category-programming')));
$job_expired->setType('full-time');
$job_expired->setCompany('Sensio Labs');
$job_expired->setLogo('sensio-labs.gif');
$job_expired->setUrl('http://www.sensiolabs.com/');
$job_expired->setPosition('Web Developer Expired');
$job_expired->setLocation('Paris, France');
$job_expired->setDescription('Lorem ipsum dolor sit amet, consectetur adipisicing elit.');
$job_expired->setHowToApply('Send your resume to lorem.ipsum [at] dolor.sit');
$job_expired->setIsPublic(true);
$job_expired->setIsActivated(true);
$job_expired->setToken('job_expired');
$job_expired->setEmail('job@example.com');
$job_expired->setCreatedAt(new \DateTime('2005-12-01'));
// ...
$em->persist($job_expired);
// ...
}
// ...
Reload fixturnya dan refresh halaman browsermu:
php app/console doctrine:fixtures:load
Refactoring
Meski semua sudah berjalan dengan baik tetapi kode yang kita buat belum cukup baik. Karena dalam konsep MVC kita tidak boleh menaruh logic aplikasi di dalam controller. Untuk itu kita harus memindah logic validasi job ke model. Di Symfony 2 semua business logic akan terletak di repository folder. Untuk menambahkan folder tersebut edit file dan tambahkan kode berikut:
Ibw\JobeetBundle\Entity\Job:
type: entity
repositoryClass: Ibw\JobeetBundle\Repository\JobRepository
# ...
Jalankan perintah berikut melalui terminal, maka doctrine akan menggenerate classRepository secara otomatis.
php app/console doctrine:generate:entities IbwJobeetBundle
Setelah classRepository dibuat buka dan edit file tersebut lalu tambahkan kode berikut ini.
src/Ibw/JobeetBundle/Repository/JobRepository.php
namespace Ibw\JobeetBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* JobRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class JobRepository extends EntityRepository
{
public function getActiveJobs($category_id = 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($category_id)
{
$qb->andWhere('j.category = :category_id')
->setParameter('category_id', $category_id);
}
$query = $qb->getQuery();
return $query->getResult();
}
}
Kemudian edit jobControllernya untuk menambahkan methos getActiveJobs yang kita buat barusan.
src/Ibw/JobeetBundle/Controller/JobController.php
// ...
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('IbwJobeetBundle:Job')->getActiveJobs();
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
- Logic untuk mendapatkan job yang masih aktif sekarang sudah berada di Model.
- Kode di controller menjadi lebih sedikit dan mudah dibaca.
- Method getActiveJobs dapat digunakan kembali di tempat lain jika dibutuhkan.
- Dapat dilakukan testing pada level kode.
Menampilkan kategori di homepage
Agara lebih memudahkan pencari lowongan kita akan menampilkan lowongan perkategori. Untuk melakukan hal tersebut hal pertama yang harus kita lakukan adalah membuat kelas repositori untuk Categori untuk itu edit file Category.orm.yml lalu tambahkan kode berikut ini.src/Ibw/JobeetBundle/Resources/config/doctrine/Category.orm.yml
Ibw\JobeetBundle\Entity\Category:
type: entity
repositoryClass: Ibw\JobeetBundle\Repository\CategoryRepository
#...
Untuk menggenerate kelas repositorinya jalankan command berikut:
php app/console doctrine:generate:entities IbwJobeetBundle
Buka file CategoryRepository dan tambahkan method getWithJobs().
src/Ibw/JobeetBundle/Repository/CategoryRepository.php
namespace Ibw\JobeetBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* CategoryRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class CategoryRepository extends EntityRepository
{
public function getWithJobs()
{
$query = $this->getEntityManager()->createQuery(
'SELECT c FROM IbwJobeetBundle:Category c LEFT JOIN c.jobs j WHERE j.expires_at > :date'
)->setParameter('date', date('Y-m-d H:i:s', time()));
return $query->getResult();
}
}
Sesuaikan aksi index
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()));
}
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'categories' => $categories
));
}
// ...
Agar kode tersebut dapat berjalan kita harus menambahkan property baru di dalam model Category, yaitu active_jobs property.
src/Ibw/JobeetBundle/Entity/Category.php
class Category
{
// ...
private $active_jobs;
// ...
public function setActiveJobs($jobs)
{
$this->active_jobs = $jobs;
}
public function getActiveJobs()
{
return $this->active_jobs;
}
}
Kita juga harus mengubah iterasi di template ke variabel Category untuk menampilkan list lowongan.
<!-- ... -->
{% block content %}
<div id="jobs">
{% for category in categories %}
<div>
<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>
</div>
{% endfor %}
</div>
{% endblock %}
Batasi jumlah berita lowongan di index
Agar lebih memudahkan pembaca maka akan lebih baik jika membatasi maksimal result menjadi hanya 10 list job per-halaman. Untuk itu kita perlu menseting $max parameter di JobRepository:getActiveJob().src/Ibw/JobeetBundle/Repository/JobRepository.php
public function getActiveJobs($category_id = null, $max = 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($category_id) {
$qb->andWhere('j.category = :category_id')
->setParameter('category_id', $category_id);
}
$query = $qb->getQuery();
return $query->getResult();
}
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(), 10));
}
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'categories' => $categories
));
}
// ...
Custom configuration
Di method index pad JobController kita masih menghardcode value dari $max. Akan jauh lebih baik jika value $max tersebut dapat terkonfigurasi. Di Symfony 2 kita dapat mengkofigurasi value dari sebuah parameter di dalam file app/config/config.yml di dalam parameters key.app/config/config.yml
# ...
parameters:
max_jobs_on_homepage: 10
Dengan begitu sekarang kita dapat mengaksesnya melalui controller.
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')));
}
return $this->render('IbwJobeetBundle:Job:index.html.twig', array(
'categories' => $categories
));
}
// ...
Dynamic fixtures
Terkadang kita akan membutuhkan data dummy dalam jumlah yang banyak aplikasi yang kita buat dapat terlihat lebih real. Berikut adalah cara untuk melakukan hal tersebut. Edit file LoadJobData.php lalu tambahkan kode berikut.src/Ibw/JobeetBundle/DataFixtures/ORM/LoadJobData.php
// ...
public function load(ObjectManager $em)
{
// ...
for($i = 100; $i <= 130; $i++)
{
$job = new Job();
$job->setCategory($em->merge($this->getReference('category-programming')));
$job->setType('full-time');
$job->setCompany('Company '.$i);
$job->setPosition('Web Developer');
$job->setLocation('Paris, France');
$job->setDescription('Lorem ipsum dolor sit amet, consectetur adipisicing elit.');
$job->setHowToApply('Send your resume to lorem.ipsum [at] dolor.sit');
$job->setIsPublic(true);
$job->setIsActivated(true);
$job->setToken('job_'.$i);
$job->setEmail('job@example.com');
$em->persist($job);
}
// ...
$em->flush();
}
// ...
Kemudian jalankan perintah berikut untuk mereload datanya kembali.
doctrine:fixtures:load
Sekarang kita bisa melihat data yang lebih banyak sehingga aplikasinya terlihat lebih hidup.
Mengamankan halaman utama
Ketika ada user yang mencoba untuk mengakses halaman dengan menggunakan id dari data lowongan yang sudah expired, maka kita harus mengirimnya ke halaman error 404. Untuk melakukan hal tersebut kita harus mengedit file JobRepository dan menambah kode berikut.src/Ibw/JobeetBundle/Repository/JobRepository.php
// ...
public function getActiveJob($id)
{
$query = $this->createQueryBuilder('j')
->where('j.id = :id')
->setParameter('id', $id)
->andWhere('j.expires_at > :date')
->setParameter('date', date('Y-m-d H:i:s', time()))
->setMaxResults(1)
->getQuery();
try {
$job = $query->getSingleResult();
} catch (\Doctrine\Orm\NoResultException $e) {
$job = null;
}
return $job;
}
Kemudian kita ubah method showAction di JobController untuk menggunakan method repository yang baru.
src/Ibw/JobeetBundle/Controller/JobController.php
// ...
showAction(){
//...
$entity = $em->getRepository('IbwJobeetBundle:Job')->getActiveJob($id);
//..
}
// ...
Ok cukup sekian dulu guys untuk hari, cheers!
No comments:
Post a Comment