新版特性

Laravel 6.0(LTS 版本) 在 Laravel 5.8 的基础上继续进行优化,包括引入语义化版本、兼容 Laravel Vapor、优化授权响应、支持任务中间件、新增懒集合、优化数据库子查询、将前端脚手架提取到独立的 Composer 包 laravel/ui、以及多个 bug 修复和可用性的提升。

语义化版本

Laravel 框架包 laravel/framework 现在遵循语义化版本标准。这使得框架得以和其他已经遵循该版本标准的 Laravel 扩展包保持一致,不过 Laravel 的发布周期将保持不变。

兼容 Laravel Vapor

Laravel 6.0 提供了对 Laravel Vapor 的兼容,这是一个针对 Laravel 应用的自动扩容无服务器部署平台。Vapor 对在 AWS Lambda 上管理 Laravel 应用以及与 SQS 队列、数据库、Redis 集群、网络、CloudFront CDN 进行交互的复杂性进行了抽象。

通过 Ignition 优化异常

Laravel 6.0 支持 Ignition,这个一个开源的错误和异常详情页项目,由 Freek Van der Herten 和 Marcel Pociot 创建并维护。相对于之前版本的错误显示,Ignition 提供了诸多便捷之处,比如优化了 Blade 错误文件和行号处理、针对常见问题的可执行解决方案、代码编辑、异常分享、以及经过优化的用户体验。

优化授权响应

在之前版本的 Laravel 中,获取并提供自定义授权消息给终端用户很困难,主要难点在于如何向终端用户解释清楚为什么特定的请求被拒绝了。在 Laravel 6.0 中,我们可以使用 Gate::inspect 方法和授权响应消息来轻松实现。例如,给定如下策略方法:

/**
 * 判断用户是否可以查看指定的航班.
 *
 * @param  \App\User  $user
 * @param  \App\Flight  $flight
 * @return mixed
 */
public function view(User $user, Flight $flight)
{
    return $this->deny('Explanation of denial.');
}

接下来我们可以通过 Gate::inspect 方法获取授权策略的响应,然后再通过响应示例的 message() 方法获取授权消息:

$response = Gate::inspect('view', $flight);

if ($response->allowed()) {
    // 用户被授权可以访问航班...
}

if ($response->denied()) {
    echo $response->message();
}

此外,当在路由或控制器中使用 $this->authorize 或者 Gate::authorize 方法时,这些自定义的消息会被自动返回给前端。

任务中间件

任务中间件允许你封装自定义的队列任务异常业务逻辑,避免在任务自身处理中混入对应样板代码。例如,在之前的 Laravel 版本中,你可能需要在频率限制回调中封装某个任务的 handle 方法处理逻辑:

/**
 * 执行任务
 *
 * @return void
 */
public function handle()
{
    Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () {
        info('Lock obtained...');

        // 处理任务...
    }, function () {
        // 无法获取到锁...

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

而在 Laravel 6.0 中,该业务逻辑可以被提取到任务中间件中,从而将你的任务 handle 方法从频率限制中解放出来:

<?php

namespace App\Jobs\Middleware;

use Illuminate\Support\Facades\Redis;

class RateLimited
{
    /**
     * 处理队列任务.
     *
     * @param  mixed  $job
     * @param  callable  $next
     * @return mixed
     */
    public function handle($job, $next)
    {
        Redis::throttle('key')
                ->block(0)->allow(1)->every(5)
                ->then(function () use ($job, $next) {
                    // Lock obtained...

                    $next($job);
                }, function () use ($job) {
                    // Could not obtain lock...

                    $job->release(5);
                });
    }
}

创建完中间件后,可以通过在任务类的 middleware 方法中返回中间件数组来将其追加到队列任务中:

use App\Jobs\Middleware\RateLimited;

/**
 * Get the middleware the job should pass through.
 *
 * @return array
 */
public function middleware()
{
    return [new RateLimited];
}

懒集合

许多开发者已经使用过 Laravel 功能强大的集合方法了,在 Laravel 6.0 中新引入了一个 LazyCollection 类来对 Collection 类进行补充,LazyCollection 底层基于 PHP 的生成器实现,适用于处理大型数据集。

例如,假设你的应用需要处理 GB 级别的日志文件,并使用 Laravel 的集合方法来解析日志,这个时候将整个日志文件一次性读取到内存显然是不合适的,这个时候懒集合类就派上用场了,它可以每次只读取文件的一小部分到内存:

use App\LogEntry;
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
})
->chunk(4)
->map(function ($lines) {
    return LogEntry::fromLines($lines);
})
->each(function (LogEntry $logEntry) {
    // Process the log entry...
});

或者,假设你需要迭代 10000 个 Eloquent 模型实例,如果使用传统的 Laravel 集合,所有 10000 个 Eloquent 模型会同时加载到内存中:

$users = App\User::all()->filter(function ($user) {
    return $user->id > 500;
});

不过,从 Laravel 6.0 开始,查询构建器的 cursor 方法已经被升级为返回 LazyCollection 实例,这样一来,我们就可以像之前一样执行一次数据库查询,但是每次只会加载一个 Eloquent 模型到内存。在这个示例中,filter 回调只有在迭代完每个用户时才会执行,从而极大减少内存的使用量:

$users = App\User::cursor()->filter(function ($user) {
    return $user->id > 500;
});

foreach ($users as $user) {
    echo $user->id;
}

Eloquent 子查询优化

Laravel 6.0 引入了多个数据库子查询优化和增强支持。例如,假设我们有一个航班目的地表 destinations 和飞向这些目的地的航班表 flightsflights 表包含了一个 arrived_at 字段来表示航班到达目的地的时间。

使用 Laravel 6.0 提供的新的子查询功能,我们可以通过一个查询获取所有的航班目的地以及最新到达这些目的地的航班名称:

return Destination::addSelect(['last_flight' => Flight::select('name')
    ->whereColumn('destination_id', 'destinations.id')
    ->orderBy('arrived_at', 'desc')
    ->limit(1)
])->get();

此外,我们还可以使用查询构建器方法 orderBy 添加的新的子查询方法基于到达目的地的最晚航班到达时间对所有航班目的地进行排序,同样,这也可以通过一次数据库查询完成:

return Destination::orderByDesc(
    Flight::select('arrived_at')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
)->get();

Laravel UI

之前版本 Laravel 提供的典型的前端脚手架代码现在被提取到独立的 Composer 扩展包 laravel/ui 中,这样一来可以让 UI 脚手架代码的开发和维护与主框架分离。所以,在默认框架脚手架代码中,现在已经剔除了 Bootstrap 和 Vue 相关代码,make:auth 命令也被提取出去。

如果想要恢复之前版本的 Vue/Bootstrap 脚手架代码,可以安装 laravel/ui 扩展包,并使用 Artisan 命令 ui 来安装前端脚手架代码:

composer require laravel/ui

php artisan ui vue --auth

上一篇: 目录索引

下一篇: 升级指南