Pada artikel ini, saya akan melihat beberapa teknik untuk membersihkan operasi pengontrol Symfony2. Beberapa di antaranya adalah dengan menggunakan beberapa fitur tambahan dalam distribusi standar, dan beberapa di antaranya adalah dengan memindahkan kode agar dipicu oleh peristiwa.
Jadi ini adalah operasi pembaruan yang cukup standar di pengontrol:
get('security.context')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) {
throw $this->createNotFoundException('Unable to find Product entity.');
}
$request = $this->getRequest();
$editForm = $this->createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $id))
);
}
return new Response(
$this->renderView(
'AcmeDemoBundle:Product:edit.html.twig',
array(
'product' => $product,
'edit_form' => $editForm->createView(),
)
)
);
}
//...
}
Operasi pengontrol pertama-tama memeriksa apakah milik pengguna saat ini ROLE_ADMIN
Peran dan lemparan serta pengecualian jika tidak dilakukan. Ambil entitas produk dari database berdasarkan id yang diteruskan. Buat formulir, ambil permintaan dari kontainer dan ikat ke formulir. Jika formulir valid, entitas akan memperbarui bidang yang terakhir diperbarui, manajer entitas akan disegarkan, dan email akan dikirim untuk memberi tahu Anda tentang perubahan tersebut. Jika perubahan produk tidak valid, tampilkan kembali formulir edit.
Pengoperasian pengontrol saat ini cukup memakan waktu, dan beberapa pekerjaan yang dilakukan, terutama mengirim email, sebenarnya tidak seharusnya dilakukan. Mari melalui beberapa langkah untuk mengimplementasikan pengontrol yang lebih efisien.
Mulailah dengan tugas yang umum pada banyak operasi, render templat, lalu kembali membalas Objek dengan hasil dapat dibuat lebih sederhana. Dengan menambahkan anotasi Templat, kita dapat mengembalikan serangkaian parameter untuk diteruskan ke templat. Dengan anotasi yang ada, saat Anda mengembalikan parameter, templat akan secara otomatis merender dan mengirimkan respons beserta hasilnya. Harap perhatikan bahwa anotasi ini adalah namespace dan memerlukan penambahan pernyataan penggunaan:
get('security.context')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) {
throw $this->createNotFoundException('Unable to find Product entity.');
}
$request = $this->getRequest();
$editForm = $this->createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $id))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Catatan selanjutnya adalah, aman Anotasi dapat digunakan sebagai pengganti pemeriksaan izin. Periksa dengan menambahkan komentar ini ROLE_ADMIN
Pelemparan peran dan pengecualian (jika diperlukan) akan terjadi tanpa memerlukan kode:
getDoctrine()->getManager();
$product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) {
throw $this->createNotFoundException('Unable to find Product entity.');
}
$request = $this->getRequest();
$editForm = $this->createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $id))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Kita dapat menggunakan Symfony2 untuk mengurai parameter tindakan pengontrol guna menghapus lebih banyak kode dari tindakan kita. Pertama, permintaan secara otomatis dimasukkan sebagai parameter metode. Parameter yang ditunjukkan melalui tipenya adalah Memerlukan Saat ini permintaan akan secara otomatis diteruskan ke tindakan dan kita dapat menghapus baris yang memintanya dari pengontrol:
getDoctrine()->getManager();
$product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) {
throw $this->createNotFoundException('Unable to find Product entity.');
}
$editForm = $this->createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $id))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Kita dapat melakukan operasi serupa pada Produk menggunakan anotasi ParamConverter. Jadi, bukannya $id
Argumen yang kita punya a $product
Jenisnya tersirat sebagai entitas produk kami. Ini secara otomatis diambil dari Doctrine dan akan memunculkan pengecualian jika tidak ditemukan. Ini sangat mirip dengan apa yang kita lakukan dalam kode, memungkinkan kita untuk menghapus kode yang mengambil entitas dari database:
createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em = $this->getDoctrine()->getManager();
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $product->getId()))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Faktanya, dalam kasus ini, jika kita tidak menetapkan opsi apa pun pada anotasi ParamConverter, kita dapat menghilangkannya sepenuhnya:
createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$product->setLastUpdated(new \DateTime);
$em = $this->getDoctrine()->getManager();
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $product->getId()))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Jika Anda tidak menggunakan Doctrine, Anda masih dapat mencapai hasil serupa dengan membuat ParamConverters Anda sendiri.
Kami sekarang telah menghapus banyak kode boilerplate yang diperlukan dalam banyak operasi, dan kami mulai dapat melihat kinerja operasi kami yang lebih baik. Tampaknya mereka melakukan terlalu banyak upaya dalam menyelamatkan perubahan produk. Melacak waktu pembaruan terakhir sebenarnya adalah tugas entitas itu sendiri, bukan pengontrol. Untungnya, untuk entitas Doktrin, hal ini mudah dicapai melalui pengembalian siklus hidup Doktrin. Aktifkan ini dengan menggunakan anotasi, lalu beri anotasi pada metode yang diperbarui lastUpdated
Bidang persisten sebelum pembaruan:
lastUpdated = new \DateTime();
}
}
Sekarang kita tidak perlu khawatir untuk selalu memperbarui pengontrol. Kami tidak hanya mengurangi tindakan pengontrol kali ini, kami juga memastikan bahwa bidang yang terakhir diperbarui tetap mutakhir, namun entitas juga diperbarui, tidak hanya dari tindakan pengontrol kami.
createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($this->container->getParameter('acme.product_email.from'))
->setTo($this->container->getParameter('acme.product_email.to'))
->setBody($this->renderView(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $product))
)
;
$this->get('mailer')->send($message);
return $this->redirect(
$this->generateUrl('product', array('id' => $product->getId()))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Langkah terakhir adalah menghapus pengiriman email tersebut, yang bisa kita lakukan kembali melalui sebuah event. Dalam hal ini kita perlu menggunakan layanan email, tetapi kita tidak ingin memasukkannya ke dalam entitas kita. Kita dapat menggunakan event pendengar mandiri dan menyuntikkan layanan dan parameter yang kita perlukan langsung ke konstruktor:
mailer = $mailer;
$this->templating = $templating;
$this->emailFrom = $emailFrom;
$this->emailTo = $emailTo;
}
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof Product) {
$message = \Swift_Message::newInstance()
->setSubject('Product updated')
->setFrom($emailFrom)
->setTo($emailTo)
->setBody($this->templating->render(
'AcmeDemoBundle:Product:email.txt.twig',
array('product' => $entity))
)
;
$this->mailer->send($message);
}
}
}
Di profil layanan, kami menentukan layanan dan parameter yang akan diteruskan ke pendengar dan menandainya sebagai pendengar acara Doktin. Sekarang pendengar akan dipanggil setelah memperbarui entitas:
%acme.product_email.from%
%acme.product_email.to%
Kami sekarang dapat menghapus pengiriman email dari pengontrol. Selain mengurangi ukuran pengontrol, kami juga memperkenalkan fleksibilitas ke dalam aplikasi. Pendengar kini dapat dengan mudah diubah atau dihapus tanpa memerlukan kode pengontrol sentuh. Kami bahkan dapat menambahkan pendengar lain untuk mengirim notifikasi SMS tanpa menyentuh pengontrol. Jika Anda tidak menggunakan Doctrine, Anda masih bisa mendapatkan manfaat ini dengan menggunakan pengelola acara untuk mengadakan acara Anda sendiri.
createForm(new ProductType(), $product);
$editForm->bind($request);
if ($editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirect(
$this->generateUrl('product', array('id' => $product->getId()))
);
}
return array(
'product' => $product,
'edit_form' => $editForm->createView(),
);
}
//...
}
Dengan menghapus sebagian besar kode boilerplate, pengontrol kami sekarang jauh lebih pendek dari sebelumnya dan lebih mudah untuk melihat apa yang terjadi. Keputusan tentang apa yang terjadi ketika suatu produk diperbarui telah dipindahkan dari pengontrol ke lapisan model.
catatan: Contoh pengontrol meluas dari pengontrol dasar dan menggunakan wadah layanan secara langsung. Saya telah menulis sebelumnya tentang membuang pengontrol sebagai layanan. Saya pikir ada manfaatnya, tetapi karena memperluas pengontrol dasar lebih umum, saya mendasarkan contoh artikel ini pada hal itu.