Kerangka Kerja PHP: Memilih Antara Symfony dan Laravel
Diterbitkan: 2022-03-11Saat ini, ketika memulai proyek baru, salah satu keputusan utama adalah memilih kerangka kerja yang tepat. Menjadi sulit membayangkan membangun aplikasi web yang kompleks dari awal saat ini tanpa aplikasi tersebut.
Banyak bahasa populer untuk pengembangan web memiliki kerangka kerja "default" mereka, seperti Ruby on Rails untuk Ruby, atau Django untuk Python. Namun, PHP tidak memiliki standar tunggal seperti itu dan memiliki beberapa opsi populer untuk dipilih.
Menurut tren Google dan GitHub, kerangka kerja PHP paling populer adalah Symfony dengan 13.7k bintang dan Laravel dengan 29k bintang (pada saat penulisan artikel ini).
Dalam artikel ini, saya akan membandingkan kedua kerangka kerja ini dan menunjukkan kepada Anda bagaimana menerapkan fitur sehari-hari yang sederhana dengan masing-masing kerangka kerja tersebut. Dengan cara ini, Anda dapat membandingkan kode contoh kehidupan nyata secara berdampingan.
Artikel ini mengasumsikan keterampilan PHP yang kuat dan pemahaman tentang paradigma arsitektur MVC, tetapi tidak diperlukan pengalaman sebelumnya dengan Symfony atau Laravel.
Para pesaing
Laravel
Ketika berbicara tentang Laravel, kami mengacu pada Laravel versi 4 dan seterusnya. Laravel 4 dirilis pada tahun 2013 dan mewakili penulisan ulang lengkap dari kerangka kerja. Fungsionalitas kerangka kerja dipisahkan menjadi komponen terpisah, yang dikelola dengan Komposer, alih-alih semuanya berada dalam satu repositori kode besar.
Laravel mendeklarasikan dirinya sebagai kerangka kerja untuk pengembangan pesat dengan sintaks yang sederhana dan indah yang mudah dipelajari, dibaca, dan dipelihara. Ini adalah kerangka kerja paling populer di tahun 2016. Menurut tren Google, ini tiga kali lebih populer daripada kerangka kerja lain, dan di GitHub, ia memiliki bintang dua kali lebih banyak daripada pesaing.
simfoni
Symfony 2 dirilis pada tahun 2011, tetapi tidak harus bingung dengan Symfony 1, yang merupakan kerangka kerja yang sama sekali berbeda dengan prinsip dasar yang berbeda. Fabien Potencier menciptakan Symfony 2, dan versi saat ini adalah 3.2, yang merupakan versi tambahan dari Symfony 2. Oleh karena itu, mereka sering disebut hanya Symfony2/3.
Seperti Laravel 4, Symfony 2 dirancang sebagai satu set komponen yang dipisahkan. Ada dua keuntungan di sini: Kami dapat mengganti komponen apa pun dalam proyek Symfony, dan kami dapat mengambil dan menggunakan komponen Symfony apa pun dalam proyek non-Symfony. Komponen Symfony dapat berfungsi sebagai contoh kode yang bagus dan digunakan di banyak proyek sumber terbuka seperti Drupal, phpBB, dan Codeception. Bahkan, Laravel sendiri menggunakan tidak kurang dari 14 komponen Symfony. Memahami Symfony dengan demikian memberi Anda banyak manfaat saat bekerja dengan proyek lain.
Instalasi Kerangka
Kedua kerangka kerja dilengkapi dengan penginstal dan pembungkus yang tersedia melalui server web bawaan PHP.
Instalasi Symfony
Instalasi Symfony sesederhana berikut ini:
# Downloading Symfony installer sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony # Granting permissions to execute installer sudo chmod a+x /usr/local/bin/symfony # Creating new Symfony project symfony new symfony_project # Launching built-in server cd symfony_project/ && php bin/console server:start
Itu dia! Instalasi Symfony Anda tersedia di URL http://localhost:8000
.
Instalasi Laravel
Proses instalasi Laravel hampir sama dan sesederhana untuk Symfony; satu-satunya perbedaan adalah Anda menginstal penginstal Laravel melalui Komposer:
# Downloading Laravel installer using Composer composer global require "laravel/installer" # Creating new Laravel project laravel new laravel_project # Launching built-in server cd laravel_project/ && php artisan serve
Anda sekarang dapat mengunjungi http://localhost:8000
dan memeriksa instalasi Laravel Anda.
Catatan: Laravel dan Symfony menjalankan port localhost yang sama (8000) secara default, jadi Anda tidak dapat menjalankan instance default ini secara bersamaan. Jangan lupa untuk menghentikan server Symfony dengan menjalankan php bin/console server:stop
sebelum meluncurkan server Laravel.
Tentang Instalasi Kerangka
Ini adalah contoh instalasi dasar. Untuk contoh penggunaan tingkat lanjut, seperti dapat mengonfigurasi proyek dengan domain lokal atau menjalankan beberapa proyek sekaligus, kedua kerangka kerja menyediakan kotak Vagrant:
- wisma Laravel,
- Rumah Symfony.
Konfigurasi Kerangka Dasar
Konfigurasi Dasar Symfony
Symfony menggunakan YAML sebagai sintaks untuk menentukan konfigurasinya. Konfigurasi default terletak di file app/config/config.yml
, dan terlihat seperti contoh berikut:
imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } framework: secret: '%secret%' router: { resource: '%kernel.root_dir%/config/routing.yml' } # ... # Twig Configuration twig: debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # ...
Untuk membuat konfigurasi khusus lingkungan, buat file app/config/config_ENV.yml
yang berisi parameter konfigurasi dasar. Berikut adalah contoh file config_dev.yml
untuk lingkungan pengembangan:
imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...
Contoh ini mengaktifkan alat web_profiler
Symfony hanya untuk lingkungan pengembangan. Alat ini membantu Anda untuk men-debug dan membuat profil aplikasi Anda langsung di jendela browser.
Dalam file konfigurasi, Anda juga dapat melihat konstruksi %secret%
. Mereka memungkinkan kita untuk menempatkan variabel khusus lingkungan dalam file parameters.yml
yang terpisah. File ini bisa unik di setiap mesin dan tidak disimpan di bawah kendali versi. Untuk kontrol versi, kami memiliki file parameters.yml.dist
khusus yang merupakan template untuk file parameters.yml
.
Berikut adalah contoh file parameters.yml
:
parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af
Konfigurasi Dasar Laravel
Konfigurasi Laravel terlihat sangat berbeda dari Symfony. Satu-satunya kesamaan yang mereka miliki adalah keduanya menggunakan file yang tidak disimpan di bawah kontrol versi ( .env
dalam kasus Laravel) dan template untuk menghasilkan file ini ( .env.example
). File ini memiliki daftar kunci dan nilai, seperti contoh berikut:
APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost
Seperti file YAML Symfony, yang satu ini untuk Laravel juga dapat dibaca manusia dan terlihat bersih. Anda juga dapat membuat file .env.testing
yang akan digunakan saat menjalankan pengujian PHPUnit.
Konfigurasi aplikasi disimpan dalam file .php
di direktori config
. Konfigurasi dasar disimpan dalam file app.php
dan konfigurasi khusus komponen disimpan dalam file <component>.php
(misalnya, cache.php
atau mail.php
). Berikut ini contoh file config/app.php
:
<?php return [ 'name' => 'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];
Konfigurasi Kerangka: Symfony vs. Laravel
Mekanisme konfigurasi aplikasi Symfony memungkinkan Anda membuat file yang berbeda untuk lingkungan yang berbeda. Selain itu, ini mencegah Anda memasukkan logika PHP yang kompleks dalam konfigurasi YAML.
Namun, Anda mungkin merasa lebih nyaman dengan sintaks konfigurasi PHP default yang digunakan Laravel dan Anda tidak perlu mempelajari sintaks YAML.
Perutean dan Pengontrol
Secara umum, aplikasi web back-end memiliki satu tanggung jawab utama: membaca setiap permintaan dan membuat respons tergantung pada konten permintaan. Kontroler adalah kelas yang bertanggung jawab untuk mengubah permintaan menjadi respons dengan memanggil metode aplikasi, sedangkan router adalah mekanisme yang membantu Anda mendeteksi kelas dan metode pengontrol mana yang harus Anda jalankan untuk permintaan tertentu.
Mari buat pengontrol yang akan menampilkan halaman posting blog yang diminta dari rute /posts/{id}
.
Perutean dan Pengontrol di Laravel
Pengontrol
<?php namespace App\Http\Controllers; use App\Post; class BlogController extends Controller { /** * Show the blog post * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { return view('post', ['post' => Post::findOrFail($id)]); } }
Router
Route::get('/posts/{id}', 'BlogController@show');
Kami telah menentukan rute untuk permintaan GET
. Semua permintaan dengan URI yang cocok dengan /posts/{id}
akan menjalankan metode show
controller BlogController
, dan akan meneruskan id
parameter ke metode tersebut. Di controller, kami mencoba menemukan objek model POST
dengan id
yang diteruskan, dan memanggil Laravel helper view()
untuk merender halaman.
Perutean dan Pengontrol di Symfony
Di Symfony, exampleController
sedikit lebih besar:
<?php namespace BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class PostController extends Controller { /** * @Route("/posts/{id}") * @param int $id * @return \Symfony\Component\HttpFoundation\Response */ public function indexAction($id) { $repository = $this->getDoctrine()->getRepository('BlogBundle:Post'); $post = $repository->find($id); if ($post === null) { throw $this->createNotFoundException(); } return $this->render('BlogBundle:Post:show.html.twig', ['post'=>$post]); } }
Anda dapat melihat bahwa kami telah menyertakan @Route("/posts/{id}”)
dalam anotasi, jadi kami hanya perlu menyertakan pengontrol dalam file konfigurasi routing.yml
:
blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /
Logika langkah demi langkah sama seperti pada kasus Laravel.
Perutean dan Pengontrol: Symfony vs. Laravel
Pada tahap ini, Anda mungkin berpikir Laravel jauh lebih bagus daripada Symfony. Ini benar, pada awalnya. Tampaknya jauh lebih baik dan lebih mudah untuk memulai. Namun, dalam aplikasi kehidupan nyata, Anda tidak boleh memanggil Doctrine dari controller. Sebagai gantinya, Anda harus memanggil layanan yang akan mencoba menemukan kiriman atau melempar HTTP 404 Exception .
Template
Laravel dikirimkan dengan mesin template yang disebut Blade dan Symfony dikirimkan dengan Twig. Kedua mesin template menerapkan dua fitur utama:
- Warisan template
- Blok atau bagian
Kedua fitur tersebut memungkinkan Anda untuk menentukan template dasar dengan bagian yang dapat diganti dan template anak yang mengisi nilai bagian tersebut.
Mari kita perhatikan contoh halaman posting blog lagi.
Mesin Template Laravel Blade
// base.blade.php <html> <head> <style></style> <title>@section('page-title') Welcome to blog! @show </title> </head> <body> <div class="container"> <h1>@yield('title')</h1> <div class="row"> @yield('content') </div> </div> </body> </html> // post.blade.php @extends('base') @section('page-title')Post {{ $post->title }} - read this and more in our blog.@endsection @section('title'){{ $post->title }}@endsection @section('content') {{ $post->content }} @endsection
Sekarang Anda dapat memberi tahu Laravel di Controller Anda untuk merender template post.blade.php
. Apakah Anda ingat panggilan view('post', …)
di contoh Controller sebelumnya? Anda tidak perlu tahu dalam kode Anda bahwa itu diwarisi dari beberapa template lain. Semuanya hanya ditentukan di template Anda, pada tingkat tampilan.
Mesin Template Ranting Symfony
// base.html.twig <html> <head> <style></style> <title>{% block page_title %} Welcome to blog! {% endblock %} </title> </head> <body> <div class="container"> <h1>{% block title %}{% endblock %}</h1> <div class="row"> {% block content %}{% endblock %} </div> </div> </body> </html> // show.html.twig {% extends '@Blog/base.html.twig' %} {% block page_title %}Post {{ post.title }} - read this and more in our blog.{% endblock %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {{ post.content }} {% endblock %}
Template: Symfony vs. Laravel
Secara struktural, template Blade dan Twig sangat mirip. Keduanya menghasilkan template ke dalam kode PHP dan bekerja dengan cepat, dan keduanya mengimplementasikan struktur kontrol, seperti pernyataan if
dan loop. Fitur terpenting yang dimiliki kedua mesin adalah mereka keluar dari output secara default, yang membantu mencegah serangan XSS.
Selain sintaks, perbedaan utama adalah bahwa Blade memungkinkan Anda untuk menyuntikkan kode PHP langsung ke template Anda dan Twig tidak. Sebagai gantinya, Twig memungkinkan Anda menggunakan filter.
Misalnya, jika Anda ingin menggunakan huruf kapital untuk string, di Blade Anda akan menentukan hal berikut:
{{ ucfirst('welcome friend') }}
Di Twig, di sisi lain, Anda akan melakukan hal berikut:
{{ 'welcome friend'|capitalize }}
Di Blade, lebih mudah untuk memperluas beberapa fungsionalitas, tetapi Twig tidak mengizinkan kode PHP langsung dalam templat.
Injeksi Ketergantungan
Aplikasi memiliki banyak layanan dan komponen yang berbeda, dengan berbagai saling ketergantungan. Anda perlu menyimpan semua informasi tentang objek yang dibuat dan dependensinya entah bagaimana.
Ini dia komponen berikutnya - Wadah Layanan. Ini adalah objek PHP yang membuat layanan yang diminta dan menyimpan informasi tentang objek yang dibuat dan dependensinya.
Mari kita perhatikan contoh berikut: Anda membuat kelas PostService
untuk mengimplementasikan metode yang bertanggung jawab untuk membuat posting blog baru. Kelas ini bergantung pada dua layanan lain: PostRepository
, yang bertanggung jawab untuk menyimpan informasi dalam database, dan SubscriberNotifier
, yang bertanggung jawab untuk memberi tahu pengguna yang berlangganan tentang posting baru. Untuk membuatnya berfungsi, Anda harus meneruskan dua layanan ini sebagai argumen konstruktor PostService
atau, dengan kata lain, Anda perlu menyuntikkan dependensi ini.
Contoh Injeksi Ketergantungan Symfony
Pertama, mari kita definisikan layanan sampel kami:
<?php // src/BlogBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { public function persist(Post $post) { // Perform save to db } }
<?php // src/BlogBundle/Service/SubscriberNotifier.php namespace BlogBundle\Service; use BlogBundle\Entity\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
<?php // src/BlogBundle/Service/PostService namespace BlogBundle\Service; use BlogBundle\Entity\Post; use BlogBundle\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }
Selanjutnya adalah konfigurasi injeksi dependensi:
# src/BlogBundle/Resources/config/services.yml services: # Our main service blog.post_service: class: BlogBundle\Service\PostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundle\Service\SubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundle\Repository\PostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundle\Entity\Post
Sekarang Anda dapat meminta layanan pos Anda di mana saja dalam kode dari objek Wadah Layanan Anda. Misalnya, di pengontrol itu bisa menjadi seperti ini:
// Controller file. $post variable defined below $this->get('blog.post_service')->create($post);
Service Container adalah komponen yang hebat, dan membantu membangun aplikasi Anda mengikuti prinsip desain SOLID.
Contoh Injeksi Ketergantungan Laravel
Jauh lebih mudah untuk mengelola dependensi di Laravel. Mari kita pertimbangkan contoh yang sama:
<?php // app/Repository/PostRepository.php namespace App\Repository; use App\Post; class PostRepository { public function persist(Post $post) { // Perform save to db } }
<?php // app/Service/SubscriberNotifier.php namespace App\Service; use App\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
<?php // app/Service/PostService.php namespace App\Service; use App\Post; use App\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; public function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }
Inilah keindahan Laravel - Anda tidak perlu membuat konfigurasi ketergantungan . Laravel secara otomatis memindai dependensi untuk PostService
dalam tipe argumen konstruktornya dan secara otomatis menyelesaikannya.

Anda juga dapat menggunakan injeksi dalam metode pengontrol Anda untuk menggunakan PostService
dengan "mengetik-petunjuk" dalam argumen metode:
<?php namespace App\Http\Controllers; use App\Post; use App\Service\PostService; class BlogController extends Controller { public function create(PostService $service) { $post = new Post(['title' => 'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }
Injeksi Ketergantungan: Symfony vs. Laravel
Deteksi otomatis Laravel bekerja dengan baik. Symfony memiliki kemampuan serupa yang disebut "autowire" yang dimatikan secara default dan dapat dihidupkan dengan menambahkan autowire: true
ke konfigurasi dependensi Anda, tetapi memerlukan beberapa konfigurasi. Cara Laravel lebih sederhana.
Pemetaan Relasional Objek (ORM)
Untuk bekerja dengan database, kedua framework dilengkapi dengan fitur Object-Relational Mapping (ORM). ORM memetakan catatan dari database ke objek dalam kode. Untuk melakukannya, Anda harus membuat model untuk setiap jenis rekaman (atau setiap tabel) di database Anda.
Symfony menggunakan Doctrine proyek pihak ketiga untuk berinteraksi dengan database, sementara Laravel menggunakan perpustakaannya sendiri Eloquent.
ORM Eloquent mengimplementasikan pola ActiveRecord untuk bekerja dengan database. Dalam pola ini, setiap model menyadari koneksi ke database dan dapat berinteraksi dengannya. Misalnya, dapat menyimpan data ke database, memperbarui, atau menghapus catatan.
Doctrine mengimplementasikan pola Data Mapper, di mana model tidak tahu apa-apa tentang database; mereka hanya mengetahui data itu sendiri. Lapisan khusus yang terpisah, EntityManager
, menyimpan semua informasi tentang interaksi antara model dan database, dan menangani semua operasi.
Mari kita ambil contoh untuk memahami perbedaannya. Katakanlah model Anda memiliki kunci id
utama, judul, konten, dan penulis. Tabel Posts hanya menyimpan author id
, jadi Anda perlu membuat relasi ke tabel Users .
Doktrin
Mari kita mulai dengan mendefinisikan model:
<?php // src/BlogBundle/Entity/User.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Table(name="user") * @ORM\Entity */ class User { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; }
<?php // src/BlogBundle/Entity/Post.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Post * * @ORM\Table(name="post") * @ORM\Entity(repositoryClass="BlogBundle\Repository\PostRepository") */ class Post { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var string * * @ORM\Column(name="content", type="text") */ protected $content; /** * @var User * * @ORM\ManyToOne(targetEntity="BlogBundle\Entity\User") * @ORM\JoinColumn(name="author_id", referencedColumnName="id") */ protected $author;
Di sini, kami membuat informasi pemetaan model dan sekarang dapat menggunakan helper untuk menghasilkan stub metode:
php bin/console doctrine:generate:entities BlogBundle
Selanjutnya, kami mendefinisikan metode repositori posting:
<?php // src/BlobBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { /** * Store post to database * * @param Post $post */ public function persist(Post $post) { $this->getEntityManager()->persist($post); $this->getEntityManager()->flush(); } /** * Search posts with given author's name * * @param string $name * @return array */ public function findByAuthorName($name) { return $this->createQueryBuilder('posts') ->select('posts') ->join('posts.author', 'author') ->where('author.name = :name') ->setParameter('name', $name) ->getQuery() ->getResult(); } }
Sekarang Anda dapat memanggil metode ini dari layanan atau, misalnya, dari PostController
:
// To search for posts $posts = $this->getDoctrine()->getRepository('BlogBundle:Post')->findByAuthorName('Karim'); // To save new post in database $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post);
Fasih
Model User dikirimkan bersama Laravel dan didefinisikan secara default, jadi Anda hanya perlu mendefinisikan satu model untuk Post .
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo('App\User', 'author_id'); } }
Itu semua untuk model. Di Eloquent, Anda tidak perlu mendefinisikan properti model, karena ini membangunnya secara dinamis berdasarkan struktur tabel database. Untuk menyimpan posting baru $post
ke dalam database, Anda perlu melakukan panggilan ini (dari pengontrol, misalnya):
$post->save();
Untuk menemukan semua posting oleh seorang penulis dengan nama tertentu, pendekatan terbaik adalah menemukan pengguna dengan namanya dan meminta semua posting pengguna:
$posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();
ORM: Symfony vs. Laravel
Berkenaan dengan ORM, Eloquent terlihat jauh lebih ramah untuk pengembang PHP dan lebih mudah dipelajari daripada Doctrine.
Pengirim Acara vs. Middleware
Salah satu hal terpenting untuk dipahami tentang kerangka kerja adalah siklus hidupnya.
Symfony dan Event Dispatcher
Untuk mengubah permintaan menjadi respons, Symfony menggunakan EventDispatcher. Akibatnya, ini memicu peristiwa siklus hidup yang berbeda dan pendengar acara khusus untuk menangani peristiwa ini. Pada awalnya, ia mengirimkan event kernel.request
yang menyertakan informasi permintaan. Pendengar default utama dari acara ini adalah RouterListener
, yang memanggil komponen router untuk menemukan aturan rute yang sesuai untuk permintaan saat ini. Setelah ini, acara lain dijalankan selangkah demi selangkah. Pendengar acara yang umum adalah pemeriksaan Keamanan, verifikasi token CSRF, dan proses logging. Jika Anda ingin menambahkan beberapa fungsi dalam siklus hidup permintaan, Anda perlu membuat EventListener
khusus dan berlangganan ke acara yang diperlukan.
Laravel dan Middleware
Laravel menggunakan solusi yang berbeda: middleware. Saya suka membandingkan middleware dengan bawang: Aplikasi Anda memiliki lapisan tertentu dan permintaan melewati lapisan ini dalam perjalanan ke pengontrol dan kembali. Jadi, jika Anda ingin memperluas logika aplikasi Anda dan menambahkan beberapa fungsionalitas dalam siklus hidup permintaan, Anda perlu menambahkan lapisan tambahan ke daftar middleware Anda, dan Laravel akan menjalankannya.
REST API
Mari kita coba membuat contoh CRUD dasar untuk mengelola posting blog:
- Buat -
POST /posts/
- Baca -
GET /posts/{id}
- Perbarui -
PATCH /posts/{id}
- Hapus -
DELETE /posts/{id}
REST API di Symfony
Symfony tidak memiliki solusi out-of-the-box yang mudah untuk pembuatan REST API yang cepat, tetapi memiliki bundel pihak ketiga yang hebat FOSRestBundle
dan JMSSerializerBundle
.
Mari kita pertimbangkan contoh kerja minimal dengan FOSRestBundle
dan JMSSerializerBundle
. Setelah Anda menginstalnya dan mengaktifkannya di AppKernel
, Anda dapat mengatur dalam konfigurasi bundel bahwa Anda akan menggunakan format JSON dan ini tidak harus disertakan dalam permintaan URL:
#app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false
Dalam konfigurasi perutean, Anda harus menentukan bahwa pengontrol ini akan mengimplementasikan sumber daya REST:
#app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest
Anda menerapkan metode bertahan dalam repositori pada contoh sebelumnya; sekarang Anda perlu menambahkan metode hapus:
// src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }
Selanjutnya, Anda perlu membuat kelas formulir untuk menerima permintaan input dan memetakannya ke model. Anda dapat melakukannya dengan menggunakan pembantu CLI:
php bin/console doctrine:generate:form BlogBundle:Post
Anda akan menerima jenis formulir yang dihasilkan dengan kode berikut:
<?php // src/BlogBundle/Form/PostType.php namespace BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PostType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundle\Entity\Post', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }
Sekarang mari kita implementasikan controller kita.
Catatan: kode yang akan saya tunjukkan kepada Anda tidak sempurna. Itu melanggar beberapa prinsip desain tetapi dapat dengan mudah di-refactored. Tujuan utamanya adalah untuk menunjukkan kepada Anda bagaimana menerapkan setiap metode, langkah demi langkah.
<?php // src/BlogBundle/Controller/PostController.php namespace BlogBundle\Controller; use BlogBundle\Entity\Post; use BlogBundle\Form\PostType; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class PostController extends FOSRestController { /** * @param $id * @return Response */ public function getPostAction($id) { $view = new View(); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $view->setData(['post' => $post]); } return $this->handleView($view); } /** * @param Request $request * @return Response */ public function postPostAction(Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = new Post; $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_CREATED); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } return $this->handleView($view); } /** * @param $id * @param Request $request * @return Response */ public function patchPostAction($id, Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } } return $this->handleView($view); } /** * @param $id * @return Response */ public function deletePostAction($id) { $view = new View(null, Response::HTTP_NOT_FOUND); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post !== null) { $this->getDoctrine()->getRepository('BlogBundle:Post')->delete($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); } return $this->handleView($view); } }
Dengan FOSRestBundle
, Anda tidak perlu mendeklarasikan rute untuk setiap metode; cukup ikuti konvensi dengan nama metode pengontrol, dan JMSSerializerBundle
akan secara otomatis mengonversi model Anda ke JSON.
REST API di Laravel
Pertama, Anda perlu menentukan rute. Anda dapat melakukan ini di bagian API
dari aturan rute untuk menonaktifkan beberapa komponen middleware default dan mengaktifkan yang lain. Bagian API
terletak di file routes/api.php
.
<?php // routes/api.php Route::resource('/posts', 'BlogController');
Dalam model, Anda harus mendefinisikan properti $fillable
untuk meneruskan variabel dalam metode membuat dan memperbarui model:
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …
Sekarang mari kita definisikan controllernya:
<?php // app/Http/Controllers/BlogController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Http\Response; class BlogController extends Controller { public function show(Post $post) { return $post; } public function store(Request $request) { $post = Post::create($request->get('post')); return response(null, Response::HTTP_CREATED, ['Location'=>'/posts/'.$post->id]); } public function update(Post $post, Request $request) { $post->update($request->get('post')); return response(null, Response::HTTP_NO_CONTENT, ['Location'=>'/posts/'.$post->id]); } public function destroy(Post $post) { $post->delete(); return response(null, Response::HTTP_NO_CONTENT); } }
Di Symfony, Anda menggunakan FosRestBundle
, yang membungkus kesalahan dalam JSON. Di Laravel, Anda harus melakukannya sendiri. Anda perlu memperbarui metode render di pengendali Pengecualian untuk mengembalikan kesalahan JSON untuk mengharapkan permintaan JSON:
<?php // app/Exceptions/Handler.php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($request->expectsJson()) { $status = 400; if ($this->isHttpException($exception)) { $status = $exception->getStatusCode(); } elseif ($exception instanceof ModelNotFoundException) { $status = 404; } $response = ['message' => $exception->getMessage(), 'code' => $exception->getCode()]; return response()->json($response, $status); } return parent::render($request, $exception); } // ... }
REST API: Symfony vs. Laravel
Seperti yang Anda lihat, untuk REST API tipikal, Laravel jauh lebih sederhana daripada Symfony.
Memilih Pemenang: Symfony atau Laravel?
Tidak ada pemenang yang jelas antara Laravel dan Symfony, karena semuanya tergantung pada tujuan akhir Anda.
Laravel adalah pilihan yang lebih baik jika:
- Ini adalah pengalaman pertama Anda dengan framework, karena mudah dipelajari dan memiliki sintaks yang lebih sederhana serta materi pembelajaran yang lebih baik.
- Anda sedang membangun produk startup dan memeriksa hipotesis Anda, karena ini bagus untuk pengembangan aplikasi yang cepat dan pengembang Laravel mudah ditemukan.
Symfony adalah pilihan terbaik jika:
- Anda sedang membangun aplikasi perusahaan yang kompleks, karena sangat skalabel, dapat dipelihara, dan terstruktur dengan baik.
- Anda sedang membangun migrasi proyek jangka panjang yang besar, karena Symfony memiliki rencana rilis yang dapat diprediksi untuk enam tahun ke depan, jadi kecil kemungkinannya akan ada kejutan.