新版特性


概述

Laravel 5.5 是一个 LTS 版本,会提供为期 2 年的 bug 修复和为期 3 年的安全修复支持。 Laravel 5.5 在 Laravel 5.4 的基础上继续进行优化:新增了包自动检测功能、API资源/转化、自动注册控制台命令、队列任务链、队列任务速率限制、基于时间的任务尝试、可渲染的邮件、可渲染和报告的异常、更加一致的异常处理、数据库测试优化、更简单的验证规则自定义、React前端预置、Route::viewRoute::redirect 方法、Memcached 和 Redis 缓存驱动"锁"、按需通知、Dusk 无痛感支持 Chrome、方便的 Blade 快捷键、优化信任的代理支持,等等。 此外,Laravel 5.5 还恰巧碰上 Laravel Horizon 的发布,这是一个基于 Redis 的 Laravel 队列后台管理与配置系统。
注:本文档只是提及一些重要的更新和优化,更多细节请参考 GitHub 上的修改日志明细。

Laravel Horizon

Horizon 为基于 Redis 的 Laravel 队列提供了一个美观的后台和代码驱动的配置。Horizon 允许你轻松监控队列系统的关键指标,例如任务吞吐量、运行时和失败任务。 所有任务进程配置都存放在一个简单独立的配置文件中,从而允许你的配置保存在源码控制系统中以便整个团队的协作。 想要了解更多关于 Horizon 的信息,可参考完整的 Horizon 文档

包自动发现

在之前版本的 Laravel 中,安装一个扩展包通常需要多个添加步骤,例如注册服务提供者到 app 配置文件,并注册相应的门面。不过,从 Laravel 5.5 开始,Laravel 可以自动发现并为你注册服务提供者和门面。 例如,你可以通过安装大名鼎鼎的 barryvdh/laravel-debugbar 扩展包到 Laravel 应用来体验这个新功能。通过 Composer 安装完成后,无需任何额外配置,调试条就可以生效:
composer require barryvdh/laravel-debugbar
扩展包开发者只需要添加自己的服务提供者和门面到包中的 composer.json 文件即可:
"extra": {
    "laravel": {
        "providers": [
            "Laravel\\Tinker\\TinkerServiceProvider"
        ]
    }
},
想要了解更多关于更新包时如何使用服务提供者和门面的自动发现功能,可以参考扩展包开发文档

API 资源

构建一个 API 时,你可能需要在 Eloquent 模型和真正返回给应用用户的 JSON 响应之间有一个转化层,Laravel 的资源类允许你简单、优雅地将模型和模型集合转化成 JSON。 一个资源类表示一个需要被转化为 JSON 结构的模型,例如,下面是一个简单的 User 资源类:
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class User extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}
当然,这只是最基本的 API 资源示例。Laravel 还提供了多种方法帮助你构建资源和资源集合,想要了解更多信息,请参考有关 API 资源的完整文档

控制台命令自动注册

当创建新的控制台命令时,不再需要手动将其添加到 Console Kernel 的 $commands 属性列表中,取而代之,在 kernel 的 commands 方法中会调用一个新的 load 方法,该方法会检索给定目录下的所有控制台命令并自动注册它们:
/**
 * Register the commands for the application.
 *
 * @return void
 */
protected function commands()
{
    $this->load(__DIR__.'/Commands');

    // ...
}

新的前端预置功能

Laravel 5.5 仍然内置了基本的 Vue 脚手架,除此之外我们新提供了一些前端预置组件,在一个全新的 Laravel 应用中,你可以使用 preset 命令将 Vue 脚手架切换到 React 脚手架:
php artisan preset react
或者,你可以使用 none 预置指令整个移除 JavaScript 和 CSS 框架脚手架。该预置命令将会使应用只留下一些原生的 Sass 文件和一些简单的 JavaScript 功能:
php artisan preset none
警告:这些命令只能在新安装的 Laravel 中使用,千万不要在已经存在尤其是在线上运行的应用代码中执行这些命令,否则后果自负!

队列任务链

任务链允许你指定需要在一个序列中运行的队列任务列表,如果这个序列中的某个任务运行失败了,那么剩下的任务就不会再运行,要执行一个队列任务链,你可以在任意分发任务中使用 withChain 方法:
ProvisionServer::withChain([
    new InstallNginx,
    new InstallPhp
])->dispatch();

队列任务频率限制

如果你的应用队列使用的是 Redis,现在起你可以通过时间或并发量来控制队列任务的执行。该功能在队列任务调用了同样设置频率限制的 API 时很有用,例如,你可以设置指定类型的任务每分钟运行 10 次:
Redis::throttle('key')->allow(10)->every(60)->then(function () {
    // Job logic...
}, function () {
    // Could not obtain lock...

    return $this->release(10);
});
注:在上面的例子中,key 可以是任意字符串,用于唯一标识你想要限制频率的任务类型。例如,你可以构建基于任务类名和其操作的 Eloquent 模型 ID 的 key。
作为可选方案,你还可以指定任务进程的最大数量来同时处理给定任务(多进程处理队列任务),这在队列任务被设计为一次一个任务编辑一个资源的时候很有用。举个例子,我们可以限制给定类型的任务同时只被一个工作进程处理:
Redis::funnel('key')->limit(1)->then(function () {
    // Job logic...
}, function () {
    // Could not obtain lock...

    return $this->release(10);
});

基于时间的任务尝试

作为定义一个任务最终失败之前尝试次数的可选方案,你现在可以一个任务的超时时间,这样的话在给定时间范围内该任务就可以尝试很多次,要定义这样的一个时间,可以添加一个 retryUntil 方法到任务类:
/**
 * Determine the time at which the job should timeout.
 *
 * @return \DateTime
 */
public function retryUntil()
{
    return now()->addSeconds(5);
}
注:你还可以在队列事件监听器中定义一个 retryUntil 方法。

验证规则对象

验证规则对象提供了一个新的、简洁的方式来添加自定义验证规则到你的应用。在之前版本的 Laravel 中,我们使用 Validator::extend方法通过闭包添加自定义验证规则,不过,这显得很笨重。在 Laravel 5.5 中,通过 Artisan 命令 make:rule 将会在 app/Rules 目录下生成一个新的验证规则:
php artisan make:rule ValidName
一个规则对象只包含两个方法:passesmessagepasses 方法接收属性值和名称,然后基于属性值是否有效返回 truefalsemessage 方法会在验证失败时返回对应验证错误消息:
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class ValidName implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return strlen($value) === 6;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The name must be six characters long.';
    }
}
定义好规则之后,就可以通过传递规则对象实例的方式来使用它:
use App\Rules\ValidName;

$request->validate([
    'name' => ['required', new ValidName],
]);

集成 Trusted Proxy

当你在运行的应用位于会终止 TLS/SSL 证书的负载均衡之后时,你可能会注意到应用有时候不会生成 HTTPS 链接,通常这是因为你的应用在80端口上被负载均衡器转发流量了,然后就不知道应该生成安全链接了。 为了解决这个问题,很多 Laravel 用户选择安装了 Trusted Proxy 扩展包,正是因为这是如此通用的使用案例,所以我们在 Laravel 5.5 中直接集成了这个扩展包。 Laravel 5.5 中新增了一个 App\Http\Middleware\TrustProxies 中间件,这个中间件允许你快速定制化需要被应用信任的代理:
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array
     */
    protected $proxies;

    /**
     * The current proxy header mappings.
     *
     * @var array
     */
    protected $headers = [
        Request::HEADER_FORWARDED => 'FORWARDED',
        Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
        Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
        Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
        Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
    ];
}

按需通知

有时候你可能需要发送通知给应用中的非用户实体,使用新的 Notification::route 方法,你可以在发送通知之前指定特别指定通知路由:
Notification::route('mail', 'taylor@laravel.com')
            ->route('nexmo', '5555555555')
            ->send(new InvoicePaid($invoice));

可渲染的邮件对象

邮件对象现在可以直接从路由返回,从而允许你快速在浏览器中预览邮件设计:
Route::get('/mailable', function () {
    $invoice = App\Invoice::find(1);

    return new App\Mail\InvoicePaid($invoice);
});

可渲染 & 可报告异常

在之前版本的 Laravel 中,如果要为给定异常渲染自定义响应,你可能不得不诉诸于在异常处理器中进行类型检查。例如,你可能在异常处理器的 render 方法中写过这样的代码:
/**
 * 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 ($exception instanceof SpecialException) {
        return response(...);
    }

    return parent::render($request, $exception);
}
在 Laravel 5.5 中,你现在可以直接在异常中定义一个 render 方法,这样你就可以直接在这个方法中设置自定义响应渲染逻辑,从而避免在异常处理器中堆积条件判断逻辑。如果你还想要为异常自定义报告逻辑,可以在该类中定义一个 report 方法:
<?php

namespace App\Exceptions;

use Exception;

class SpecialException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
        //
    }

    /**
     * Report the exception.
     *
     * @param  \Illuminate\Http\Request
     * @return void
     */
    public function render($request)
    {
        return response(...);
    }
}

请求验证

Illuminate\Http\Request 对象现在提供了一个 validate 方法,该方法允许你快速验证来自路由闭包或控制器的输入请求:
use Illuminate\Http\Request;

Route::get('/comment', function (Request $request) {
    $request->validate([
        'title' => 'required|string',
        'body' => 'required|string',
    ]);

    // ...
});

一致的异常处理

验证异常处理现在在整个框架中都是一致的,以前版本里,在框架中有多个位置需要定制化代码以改变针对 JSON 验证错误响应的默认格式。在 Laravel 5.5 版本中,针对 JSON 验证响应的默认格式遵循以下约定:
{
    "message": "The given data was invalid.",
    "errors": {
        "field-1": [
            "Error 1",
            "Error 2"
        ],
        "field-2": [
            "Error 1",
            "Error 2"
        ],
    }
}
所有的 JSON 验证错误格式都可以通过在 App\Exceptions\Handler 类中定义单独的方法进行控制。例如,下面的自定义代码将使用 Laravel 5.4 约定来格式化 JSON 验证响应:
use Illuminate\Validation\ValidationException;

/**
 * Convert a validation exception into a JSON response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Validation\ValidationException  $exception
 * @return \Illuminate\Http\JsonResponse
 */
protected function invalidJson($request, ValidationException $exception)
{
    return response()->json($exception->errors(), $exception->status);
}

缓存锁

Redis 和 Memcached 缓存驱动现在支持获取和释放原子"锁",该功能提供了一个获取任意锁的简单方法而不必担心任何竞争条件。例如,在执行一个任务之前,你可能想要获取一个锁,这样,任何其他进程都不能再尝试这个已经在执行的任务:
if (Cache::lock('lock-name', 60)->get()) {
    // Lock obtained for 60 seconds, continue processing...

    Cache::lock('lock-name')->release();
} else {
    // Lock was not able to be obtained...
}
或者,你可以传递一个闭包到 get 方法,这个闭包只有在可以获取锁的时候才会执行,并且在闭包执行之后会自动释放锁:
Cache::lock('lock-name', 60)->get(function () {
    // Lock obtained for 60 seconds...
});
此外,在锁释放之前你会一直处于"阻塞"状态:
if (Cache::lock('lock-name', 60)->block(10)) {
    // Wait for a maximum of 10 seconds for the lock to become available...
}

Blade 优化

当你需要定义简单的自定义条件语句时,编写自定义指令的复杂性往往大于必要性,因为这个原因,Blade 现在提供了一个 Blade::if 方法帮助你使用闭包快速定义自定义条件指令。例如,让我们来定义一个自定义条件来检查当前的应用环境,我们可以在 AppServiceProviderboot 方法中完成这个功能:
use Illuminate\Support\Facades\Blade;

/**
 * Perform post-registration booting of services.
 *
 * @return void
 */
public function boot()
{
    Blade::if('env', function ($environment) {
        return app()->environment($environment);
    });
}
定义好自定义条件后,就可以在模板中使用了:
@env('local')
    // The application is in the local environment...
@else
    // The application is not in the local environment...
@endenv
除了简化自定义 Blade 条件指令之外,我们还新增了几个快捷指令来快速检查当前用户的认证状态(是否登录):
@auth
    // The user is authenticated...
@endauth

@guest
    // The user is not authenticated...
@endguest

新的路由方法

如果你需要定义一个重定向到另一个URI的路由,那么现在你可以使用 Route::redirect 来实现。这个方法很方便,有了它你不再需要定义一个完整的路由或者控制器来执行一个简单的重定向:
Route::redirect('/here', '/there', 301);
如果你的路由只需要返回一个视图,现在你可以使用 Route::view 方法,和 redirect 方法类似,有了这个方法之后,你不再需要为一个简单的视图定义一个完整的路由或控制器。view 方法接收一个 URI 作为其第一个参数以及一个视图名作为第二个参数,此外,你可以提供一个数组数据传递到视图作为一个可选的第三个参数:
Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

"粘性"数据库连接

sticky 选项 当配置读/写数据库连接时,支持一个新的配置项 sticky
'mysql' => [
    'read' => [
        'host' => '192.168.1.1',
    ],
    'write' => [
        'host' => '196.168.1.2'
    ],
    'sticky'    => true,
    'driver'    => 'mysql',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => '',
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix'    => '',
],
sticky 选项是可选的值,可用于允许在当前请求生命周期内立即读取刚刚写入数据库的记录。如果 sticky 选项被开启并且在当前请求生命周期内在数据库上进行了一次"写"操作,任意后续的"读"操作将会使用"写"连接,这样就可以确保任何在当前请求周期内写入的数据可以立即在同一个请求生命周期内被正确地从数据库读取。这可以看作是解决分布式数据库主从延迟的一种方案,至于是否启用这样的功能最终取决于你。

点赞 取消点赞 收藏 取消收藏

<< 上一篇: 目录索引

>> 下一篇: 升级指南