PHP 框架:在 Symfony 和 Laravel 之間進行選擇
已發表: 2022-03-11今天,當開始一個新項目時,關鍵決定之一就是選擇正確的框架。 現在很難想像沒有人從頭開始構建一個複雜的 Web 應用程序。
許多用於 Web 開發的流行語言都有其“默認”框架,例如用於 Ruby 的 Ruby on Rails 或用於 Python 的 Django。 然而,PHP 沒有這樣的單一默認值,並且有多個流行的選項可供選擇。
根據 Google 趨勢和 GitHub,最流行的 PHP 框架是擁有 13.7k 星的 Symfony 和擁有 29k 星的 Laravel(在撰寫本文時)。
在本文中,我將比較這兩個框架,並向您展示如何使用每個框架實現簡單的日常功能。 這樣,您可以並排比較實際示例的代碼。
本文假定您具備強大的 PHP 技能和對 MVC 架構範式的理解,但不需要之前使用過 Symfony 或 Laravel 的經驗。
競爭者
拉拉維爾
當談到 Laravel 時,我們指的是 Laravel 版本 4 及更高版本。 Laravel 4 於 2013 年發布,代表了對框架的完全重寫。 框架的功能被分離成單獨的組件,由 Composer 管理,而不是所有東西都在一個巨大的代碼存儲庫中。
Laravel 宣稱自己是一個快速開發的框架,其語法簡單漂亮,易於學習、閱讀和維護。 它是 2016 年最流行的框架。根據 Google 趨勢,它的受歡迎程度是其他框架的 3 倍,在 GitHub 上,它的 star 是競爭對手的 2 倍。
Symfony
Symfony 2 於 2011 年發布,但千萬不要將它與 Symfony 1 混淆,後者是一個完全不同的框架,具有不同的基本原理。 Fabien Potencier 創建了 Symfony 2,當前版本是 3.2,是 Symfony 2 的增量版本。因此,它們通常被簡稱為 Symfony2/3。
與 Laravel 4 一樣,Symfony 2 被設計為一組解耦的組件。 這裡有兩個好處:我們可以替換 Symfony 項目中的任何組件,並且我們可以在非 Symfony 項目中獲取和使用任何 Symfony 組件。 Symfony 組件可以作為很好的代碼示例,它們被用於許多開源項目,例如 Drupal、phpBB 和 Codeception。 事實上,Laravel 本身使用了不下 14 個 Symfony 組件。 因此,理解 Symfony 會給您在處理其他項目時帶來很多好處。
框架安裝
這兩個框架都帶有可通過 PHP 內置 Web 服務器獲得的安裝程序和包裝程序。
Symfony 安裝
Symfony 的安裝非常簡單,如下所示:
# 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
而已! 您的 Symfony 安裝在 URL http://localhost:8000
上可用。
Laravel 安裝
Laravel 的安裝過程幾乎和 Symfony 一樣簡單; 唯一的區別是你通過 Composer 安裝 Laravel 的安裝程序:
# 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
你現在可以訪問http://localhost:8000
並檢查你的 Laravel 安裝。
注意: Laravel 和 Symfony 默認運行在同一個 localhost 端口(8000),所以你不能讓這些默認實例同時運行。 在啟動 Laravel 服務器之前,不要忘記通過運行php bin/console server:stop
來停止 Symfony 服務器。
關於框架安裝
這些是基本安裝的示例。 對於更高級的使用示例,例如能夠使用本地域配置項目或一次運行多個項目,兩個框架都提供了 Vagrant 框:
- Laravel宅基地,
- Symfony 家園。
基本框架配置
Symfony 基本配置
Symfony 使用 YAML 作為指定其配置的語法。 默認配置位於app/config/config.yml
文件中,類似於以下示例:
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%' # ...
要創建特定於環境的配置,請創建包含基本配置參數的文件app/config/config_ENV.yml
。 以下是開發環境的config_dev.yml
文件示例:
imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...
這個例子只為開發環境打開web_profiler
Symfony 工具。 此工具可幫助您直接在瀏覽器窗口中調試和分析您的應用程序。
在配置文件中,您還可以注意到%secret%
結構。 它們允許我們將特定於環境的變量放在單獨的parameters.yml
文件中。 此文件在每台機器上可能是唯一的,並且不受版本控制存儲。 對於版本控制,我們有一個特殊的parameters.yml.dist
文件,它是parameters.yml
文件的模板。
以下是parameters.yml
文件的示例:
parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af
Laravel 基本配置
Laravel 配置看起來與 Symfony 的配置非常不同。 它們唯一的共同點是它們都使用未存儲在版本控制下的文件(在.env
案例中為 .env )和生成此文件的模板( .env.example
)。 該文件有一個鍵和值列表,如下例所示:
APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost
就像 Symfony YAML 文件一樣,這個用於 Laravel 的文件也是人類可讀的並且看起來很乾淨。 您還可以創建運行 PHPUnit 測試時使用的.env.testing
文件。
應用程序配置存儲在config
目錄中的.php
文件中。 基本配置存儲在app.php
文件中,特定於組件的配置存儲在<component>.php
文件中(例如cache.php
或mail.php
)。 下面是一個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', // ... ];
框架配置:Symfony 與 Laravel
Symfony 的應用程序配置機制允許您為不同的環境創建不同的文件。 此外,它還可以防止您在 YAML 配置中註入複雜的 PHP 邏輯。
但是,您可能對 Laravel 使用的默認 PHP 配置語法感覺更舒服,而且您不必學習 YAML 語法。
路由和控制器
通常,後端 Web 應用程序有一個主要職責:讀取每個請求並根據請求的內容創建響應。 控制器是一個類,負責通過調用應用程序方法將請求轉換為響應,而路由器是一種機制,可幫助您檢測應為特定請求執行哪個控制器類和方法。
讓我們創建一個控制器來顯示從/posts/{id}
路由請求的博客文章頁面。
Laravel 中的路由和控制器
控制器
<?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)]); } }
路由器
Route::get('/posts/{id}', 'BlogController@show');
我們已經為GET
請求定義了路由。 URI 與/posts/{id}
匹配的所有請求都將執行BlogController
控制器的show
方法,並將參數id
傳遞給該方法。 在控制器中,我們試圖通過傳遞的id
找到模型POST
的對象,並調用 Laravel 助手view()
來渲染頁面。
Symfony 中的路由和控制器
在 Symfony 中, exampleController
稍微大一點:
<?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]); } }
你可以看到我們已經在註解中包含@Route("/posts/{id}”)
,所以我們只需要在routing.yml
配置文件中包含控制器:
blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /
一步一步的邏輯與 Laravel 案例中的相同。
路由和控制器:Symfony 與 Laravel
在這個階段,你可能會認為 Laravel 比 Symfony 好得多。 這是真的,在一開始。 它看起來更好,更容易開始。 但是,在現實生活中的應用程序中,您不應該從控制器調用 Doctrine。 相反,您應該調用將嘗試查找帖子或拋出HTTP 404 Exception的服務。
模板
Laravel 附帶了一個名為 Blade 的模板引擎,而 Symfony 附帶了 Twig。 兩個模板引擎都實現了兩個主要功能:
- 模板繼承
- 塊或部分
這兩個功能都允許您定義一個基本模板,其中包含可覆蓋的部分和填充這些部分的值的子模板。
讓我們再次考慮博客文章頁面的示例。
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
現在你可以在 Controller 中告訴 Laravel 渲染模板post.blade.php
。 你還記得前面控制器示例中的view('post', …)
調用嗎? 您無需在代碼中知道它是從其他模板繼承的。 它只在您的模板中,在視圖級別上定義。
Symfony Twig 模板引擎
// 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 %}
模板:Symfony 與 Laravel
在結構上,Blade 和 Twig 模板非常相似。 兩者都將模板生成到 PHP 代碼中並且工作速度很快,並且都實現了控制結構,例如if
語句和循環。 這兩個引擎最重要的特性是它們默認轉義輸出,這有助於防止 XSS 攻擊。
除了語法之外,主要區別在於 Blade 允許您將 PHP 代碼直接注入到模板中,而 Twig 不允許。 相反,Twig 允許您使用過濾器。
例如,如果你想大寫一個字符串,在 Blade 中你可以指定以下內容:
{{ ucfirst('welcome friend') }}
另一方面,在 Twig 中,您將執行以下操作:
{{ 'welcome friend'|capitalize }}
在 Blade 中,擴展某些功能更容易,但 Twig 不允許在模板中使用任何直接的 PHP 代碼。
依賴注入
應用程序有許多不同的服務和組件,具有各種相互依賴關係。 您需要以某種方式存儲有關已創建對象及其依賴項的所有信息。
這是我們的下一個組件——服務容器。 它是一個 PHP 對象,用於創建請求的服務並存儲有關創建的對象及其依賴項的信息。
讓我們考慮以下示例:您正在創建一個PostService
類來實現負責創建新博客文章的方法。 這個類依賴於另外兩個服務: PostRepository
,它負責在數據庫中存儲信息,以及SubscriberNotifier
,它負責通知訂閱用戶新的帖子。 要使其工作,您需要將這兩個服務作為PostService
的構造函數參數傳遞,或者換句話說,您需要注入這些依賴項。
Symfony 依賴注入示例
首先,讓我們定義我們的示例服務:
<?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); } }
接下來是依賴注入配置:
# 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
現在,您可以在您的服務容器對象的代碼中的任何位置請求您的後期服務。 例如,在控制器中它可能是這樣的:
// Controller file. $post variable defined below $this->get('blog.post_service')->create($post);
Service Container 是一個很棒的組件,它有助於按照 SOLID 設計原則構建您的應用程序。
Laravel 依賴注入示例
在 Laravel 中管理依賴項要容易得多。 讓我們考慮同樣的例子:
<?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); } }
這就是 Laravel 的美妙之處——你不需要創建依賴配置。 PostService
在其構造函數參數類型中自動掃描 PostService 的依賴關係並自動解析它們。
您還可以在控制器方法中使用注入來使用PostService
,方法是在方法參數中“類型提示”它:
<?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); } }
依賴注入:Symfony 與 Laravel
Laravel 的自動檢測效果很好。 Symfony 有一個類似的功能,叫做“autowire”,默認情況下是關閉的,可以通過添加autowire: true
到你的依賴配置來打開,但它需要一些配置。 Laravel 的方式更簡單。

對象關係映射 (ORM)
為了使用數據庫,這兩個框架都帶有對象關係映射 (ORM) 功能。 ORM 將數據庫中的記錄映射到代碼中的對象。 為此,您必須為數據庫中的每種記錄類型(或每個表)創建模型。
Symfony 使用第三方項目 Doctrine 與數據庫交互,而 Laravel 使用自己的庫 Eloquent。
Eloquent ORM 實現了 ActiveRecord 模式來處理數據庫。 在這種模式中,每個模型都知道與數據庫的連接並可以與之交互。 例如,它可以將數據保存到數據庫、更新或刪除記錄。
Doctrine 實現了 Data Mapper 模式,其中模型對數據庫一無所知; 他們只知道數據本身。 一個特殊的獨立層EntityManager
存儲有關模型和數據庫之間交互的所有信息,並處理所有操作。
讓我們舉個例子來理解其中的區別。 假設您的模型有一個主id
鍵、標題、內容和作者。 Posts表僅存儲作者id
,因此您需要創建與Users表的關係。
教義
讓我們從定義模型開始:
<?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;
在這裡,我們創建了模型映射信息,現在可以使用幫助程序來生成方法存根:
php bin/console doctrine:generate:entities BlogBundle
接下來,我們定義後存儲庫方法:
<?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(); } }
現在您可以從服務或例如從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);
雄辯
User模型是 Laravel 自帶的,它是默認定義的,所以你只需要為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'); } }
這就是模型的全部內容。 在 Eloquent 中,您不必定義模型屬性,因為它會根據數據庫表結構動態構建它們。 要將新帖子$post
存儲到數據庫中,您需要進行此調用(例如,從控制器):
$post->save();
要查找具有給定名稱的作者的所有帖子,最好的方法是找到具有其名稱的用戶並請求所有用戶的帖子:
$posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();
ORM:Symfony 與 Laravel
關於 ORM,Eloquent 看起來對 PHP 開發人員更友好,也比 Doctrine 更容易學習。
事件調度程序與中間件
了解框架的最重要的事情之一是它的生命週期。
Symfony 和事件調度器
為了將請求轉換為響應,Symfony 使用 EventDispatcher。 因此,它會觸發不同的生命週期事件和特殊事件偵聽器來處理這些事件。 一開始,它會分派包含請求信息的kernel.request
事件。 此事件的主要默認偵聽器是RouterListener
,它調用路由器組件為當前請求找到合適的路由規則。 在此之後,將逐步執行其他事件。 典型的事件偵聽器是安全檢查、CSRF 令牌驗證和日誌記錄過程。 如果你想在請求生命週期中添加一些功能,你需要創建一個自定義的EventListener
並訂閱必要的事件。
Laravel 和中間件
Laravel 使用了不同的解決方案:中間件。 我喜歡將中間件比作洋蔥:您的應用程序具有某些層,並且請求在到達控制器並返回的途中通過這些層。 所以,如果你想擴展你的應用程序邏輯並在請求生命週期中添加一些功能,你需要在你的中間件列表中添加一個額外的層,Laravel 會執行它。
REST API
讓我們嘗試創建一個基本的 CRUD 示例來管理博客文章:
- 創建 -
POST /posts/
- 閱讀 -
GET /posts/{id}
- 更新 -
PATCH /posts/{id}
- 刪除 -
DELETE /posts/{id}
Symfony 中的 REST API
Symfony 沒有用於快速創建 REST API 的簡單的開箱即用解決方案,但它有很棒的第三方包FOSRestBundle
和JMSSerializerBundle
。
讓我們考慮使用FOSRestBundle
和JMSSerializerBundle
的最小工作示例。 在您安裝它們並在AppKernel
中打開它們之後,您可以在捆綁配置中設置您將使用 JSON 格式並且這不必包含在 URL 請求中:
#app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false
在路由配置中,您應該指定此控制器將實現 REST 資源:
#app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest
在前面的示例中,您在存儲庫中實現了一個 persist 方法; 現在您需要添加一個刪除方法:
// src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }
接下來,您需要創建一個表單類來接受輸入請求並將它們映射到模型。 您可以使用 CLI 幫助程序來做到這一點:
php bin/console doctrine:generate:form BlogBundle:Post
您將收到帶有以下代碼的生成表單類型:
<?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'; } }
現在讓我們實現我們的控制器。
注意:我將向您展示的代碼並不完美。 它違反了一些設計原則,但很容易重構。 主要目的是向您展示如何逐步實現每種方法。
<?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); } }
使用FOSRestBundle
,您不需要為每個方法聲明一個路由; 只需遵循控制器方法名稱的約定, JMSSerializerBundle
就會自動將您的模型轉換為 JSON。
Laravel 中的 REST API
首先,您需要定義路線。 您可以在路由規則的API
部分執行此操作,以關閉一些默認中間件組件並打開其他組件。 API
部分位於routes/api.php
文件中。
<?php // routes/api.php Route::resource('/posts', 'BlogController');
在模型中,您應該定義$fillable
屬性以在模型的創建和更新方法中傳遞變量:
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …
現在讓我們定義控制器:
<?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); } }
在 Symfony 中,您使用的是FosRestBundle
,它將錯誤包裝在 JSON 中。 在 Laravel 中,你需要自己做。 您需要更新 Exception 處理程序中的 render 方法以返回預期 JSON 請求的 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 與 Laravel
如您所見,對於典型的 REST API,Laravel 比 Symfony 簡單得多。
選擇贏家:Symfony 還是 Laravel?
Laravel 和 Symfony 之間沒有明顯的贏家,因為一切都取決於你的最終目標。
如果滿足以下條件,Laravel 是更好的選擇:
- 這是您對該框架的第一次體驗,因為它易於學習,並且具有更簡單的語法和更好的學習材料。
- 你正在構建一個啟動產品並檢查你的假設,因為它有利於快速應用程序開發,並且很容易找到 Laravel 開發人員。
如果滿足以下條件,Symfony 是最佳選擇:
- 您正在構建一個複雜的企業應用程序,因為它非常可擴展、可維護且結構良好。
- 您正在構建一個大型長期項目的遷移,因為 Symfony 對未來六年有可預測的發布計劃,因此不太可能出現任何意外。