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.phpmail.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。 兩個模板引擎都實現了兩個主要功能:

  1. 模板繼承
  2. 塊或部分

這兩個功能都允許您定義一個基本模板,其中包含可覆蓋的部分和填充這些部分的值的子模板。

讓我們再次考慮博客文章頁面的示例。

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 設計原則構建您的應用程序。

相關:使用 Symfony 組件進行真正的依賴注入

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 與 Laravel 生命週期

了解框架的最重要的事情之一是它的生命週期。

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 的簡單的開箱即用解決方案,但它有很棒的第三方包FOSRestBundleJMSSerializerBundle

讓我們考慮使用FOSRestBundleJMSSerializerBundle的最小工作示例。 在您安裝它們並在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 對未來六年有可預測的發布計劃,因此不太可能出現任何意外。