本文概述
- 竞争者
- 框架安装
- 基本框架配置
- 路由和控制器
- 范本
- 依赖注入
- 对象关系映射(ORM)
- 事件调度程序与中间件
- REST API
- 选择优胜者:Symfony或Laravel?
许多用于Web开发的流行语言都有其” 默认” 框架, 例如用于Ruby的Ruby on Rails或用于Python的Django。但是, PHP没有这样的默认设置, 并且有多个常用选项可供选择。
根据Google的趋势和GitHub, 最受欢迎的PHP框架是具有13.7万星的Symfony和具有29k星的Laravel(在撰写本文时)。
在本文中, 我将比较这两个框架, 并向你展示如何分别实现简单的日常功能。这样, 你可以并排比较实际示例的代码。
本文假定你具有强大的PHP技能和对MVC架构范例的理解, 但是不需要先前具有Symfony或Laravel的经验。
竞争者 拉拉韦尔
在谈到Laravel时, 我们指的是Laravel版本4及更高版本。 Laravel 4于2013年发布, 代表了对框架的完全重写。框架的功能被分离为单独的组件, 这些组件由Composer管理, 而不是将所有组件都放在一个庞大的代码存储库中。
Laravel宣称自己是一个快速发展的框架, 它具有简单易懂的语法, 易于学习, 阅读和维护。它是2016年最受欢迎的框架。根据Google的趋势, 它的流行度是其他框架的三倍, 而在GitHub上, 它的明星数是竞争对手的两倍。
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
而已!你可以在URL http:// localhost:8000上使用Symfony安装。
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都在同一本地主机端口(8000)上运行, 因此你不能让这些默认实例同时运行。在启动Laravel服务器之前, 请不要忘记通过运行php bin / console服务器来停止Symfony服务器:
关于框架安装
这些是基本安装的示例。对于更高级的用法示例, 例如能够使用本地域配置项目或一次运行多个项目, 这两个框架都提供了Vagrant框:
- Laravel Homestead,
- Symfony Homestead。
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%构造。它们使我们可以将特定于环境的变量放在单独的parameter.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的配置完全不同。它们唯一的共同点是它们都使用了不在版本控制下存储的文件(在Laravel中为.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的该文件也易于阅读, 看上去也很干净。你还可以创建.env.testing文件, 该文件将在运行PHPUnit测试时使用。
应用程序配置存储在config目录中的.php文件中。基本配置存储在app.php文件中, 而特定于组件的配置存储在< component> .php文件中(例如, cache.php或mail.php)。这是config / app.php文件的示例:
<
?phpreturn [
'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中的路由和控制器
控制者
<
?phpnamespace 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}', '[email
protected]');
我们已经定义了GET请求的路由。 URI匹配/ posts / {id}的所有请求都将执行BlogController控制器的show方法, 并将参数id传递给该方法。在控制器中, 我们尝试查找带有传递的id的POST模型的对象, 并调用Laravel helper view()呈现页面。
Symfony中的路由和控制器
在Symfony中, exampleController更大一些:
<
?phpnamespace 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异常的服务。
范本 Laravel附带了名为Blade的模板引擎, 而Symfony附带了Twig。这两个模板引擎都实现了两个主要功能:
- 模板继承
- 块或节
让我们再次考虑博客帖子页面的示例。
Laravel刀片模板引擎
// 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 [email
protected]@section('title'){{ $post->
title }}@endsection@section('content')
{{ $post->
content }}
@endsection
现在, 你可以告诉Controller中的Laravel渲染模板post.blade.php。你还记得上一个Controller示例中的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);
服务容器是一个很棒的组件, 它有助于遵循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);
}
}
【PHP框架(在Symfony和Laravel之间进行选择)】这里是Laravel的优点-你无需创建依赖项配置。 Laravel在其构造函数参数类型中自动扫描PostService的依赖项, 并自动解析它们。
你也可以在控制器方法中使用注入, 以通过在方法参数中对其进行” 类型提示” 来使用PostService:
<
?phpnamespace 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具有类似的功能, 称为” 自动装配” , 默认情况下处于关闭状态, 可以通过添加自动装配来打开:true依赖项配置, 但需要一些配置。 Laravel方法更简单。
对象关系映射(ORM) 为了使用数据库, 这两个框架都具有对象关系映射(ORM)功能。 ORM将记录从数据库映射到代码中的对象。为此, 必须为数据库中的每种记录类型(或每种表)创建模型。
Symfony使用第三方项目Doctrine与数据库进行交互, 而Laravel使用其自己的库Eloquent。
雄辩的ORM实施ActiveRecord模式以与数据库一起使用。在这种模式下, 每个模型都知道与数据库的连接并可以与其进行交互。例如, 它可以将数据保存到数据库, 更新或删除记录。
Doctrine实现了Data Mapper模式, 其中模型对数据库一无所知。他们只知道数据本身。特殊的单独层EntityManager存储有关模型和数据库之间交互的所有信息, 并处理所有操作。
让我们举个例子来了解差异。假设你的模型具有主ID密钥, 标题, 内容和作者。 “ 帖子” 表仅存储作者ID, 因此你需要创建与” 用户” 表的关系。
教义
首先定义模型:
<
?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);
雄辩
Laravel附带有User模型, 并且默认情况下已定义该模型, 因此你只需为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与拉拉韦尔
关于ORM, 与Doctrine相比, Eloquent对PHP开发人员而言更加友好并且更易于学习。
事件调度程序与中间件
文章图片
要了解框架, 最重要的事情之一就是框架的生命周期。
Symfony和事件调度程序
要将请求转换为响应, Symfony使用EventDispatcher。因此, 它将触发不同的生命周期事件和特殊事件侦听器来处理这些事件。首先, 它调度包含请求信息的kernel.request事件。此事件的主要默认侦听器是RouterListener, 它调用路由器组件以为当前请求找到合适的路由规则。此后, 将逐步执行其他事件。典型的事件侦听器是安全检查, CSRF令牌验证和日志记录过程。如果要在请求生命周期中添加某些功能, 则需要创建一个自定义EventListener并将其订阅到必要的事件。
Laravel和中间件
Laravel使用另一种解决方案:中间件。我喜欢将中间件与洋葱进行比较:你的应用程序具有某些层, 并且请求在通过这些层的过程中将传递到控制器并返回。因此, 如果你想扩展应用程序逻辑并在请求生命周期中添加一些功能, 则需要在中间件列表中添加一个附加层, Laravel将执行它。
REST API 让我们尝试创建一个基本的CRUD示例来管理博客文章:
- 创建-POST /帖子/
- 阅读-GET /帖子/ {id}
- 更新-PATCH / posts / {id}
- 删除-删除/ posts / {id}
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方法。现在你需要添加一个delete方法:
// 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制定了未来六年的可预测发布计划, 因此不太可能出现任何意外。
推荐阅读
- JUnit健壮的单元和集成测试指南
- 如何修复Windows 11中的音频嗡嗡声(解决办法介绍)
- 微信h5页面中跳转下载第三方app的办法
- 桌面小部件AppWidgetProvider简单分析
- The module is an Android project without build variants, and cannot be built
- android studio - Indexing paused due to batch updated
- react_app 项目开发 _前后端分离_后台管理系统_开始
- [译]A Bayesian Approach to Digital Matting
- Androidstudio_LinearLayout