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 对未来六年有可预测的发布计划,因此不太可能出现任何意外。