新版特性

声明:Laravel 7 于 2020 年 3 月 3 日正式发布,不是 LTS 版本,所以 bug 修复支持会持续半年,到 2020 年 9 月 3 日,也就是下一个主版本 Laravel 8 发布前后,安全修复则会持续一年,到 2021 年 3 月 3 日。

Laravel 7 在 Laravel 6.x 版本基础上继续进行优化,主要包含了以下更新:

  • Laravel Airlock(轻量级 API 认证解决方案)
  • 路由匹配速度优化
  • 自定义 Eloquent 转化类型
  • Blade 组件标签
  • 字符串操作方法优化
  • 为开发者提供的 HTTP 客户端
  • 原生 CORS 支持
  • 路由模型绑定优化
  • 桩自定义(自定义生成类代码)
  • 数据库队列优化
  • 同时支持多个邮件驱动
  • 查询时类型转化
  • 新增 artisan test 命令
  • 其他 bug 修复和可用性优化

下面我们来简单介绍下上述新增的特性及代码优化:

Laravel Airlock

作者:Taylor Otwell

Laravel Airlock 为 SPA(单页面应用)、移动应用以及基于令牌的简单 API 系统提供了轻量级的认证解决方案,Airlock 允许为应用的每个用户账号生成多个 API 令牌(Token),然后对这些令牌进行授权并设置作用域,从而定义它们被允许执行的操作,就像 Github Access Token 那样,这样一来就可以在请求中带上这些令牌以便访问对应的 API 接口。

后续我们会在 Airlock 文档中详细介绍 Laravel Airlock 的实现原理和使用明细。

自定义 Eloquent 转化

作者:Taylor Otwell

Laravel 已经提供了大量内置的、有用的 Eloquent 转化类型,不过,有的时候,你可能还是需要自定义转化类型才能实现特定的功能。现在你可以通过定义一个实现 CastsAttributes 接口的类来完成这个自定义。

实现该接口的类必须定义 getset 方法,get 方法负责将取自数据库的原生值转换为对应的转化类型值,而与之相反,set 方法负责将转化类型值转换为可以存储到数据库的原生值。

例如,我们可以像下面这样自定义一个转化类型来重新实现内置的 json 转化类型:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Json implements CastsAttributes
{
    /**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return array
     */
    public function get($model, $key, $value, $attributes)
    {
        return json_decode($value, true);
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  array  $value
     * @param  array  $attributes
     * @return string
     */
    public function set($model, $key, $value, $attributes)
    {
        return json_encode($value);
    }
}

定义好自定义转化类型后,可以通过如下方式将其应用到某个模型属性:

<?php

namespace App;

use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'options' => Json::class,
    ];
}

这样,当我们从从数据库获取 users 表的 options 字段值时,会自动对其进行 JSON 解码完成类型转化,而将该字段值存储到数据库时,会自动对其进行 JSON 编码。

更加详细的使用明细可以参考 Eloquent 相关文档

Blade 组件标签 & 优化

作者:SpatieMarcel PociotCaleb PorzioDries VintsTaylor Otwell

Blade 组件现在被重构为允许基于标签进行渲染、支持属性管理、组件类、内联视图组件等,因此代码调整很多,可以参考完整的 Blade 组件文档了解更多细节。

综上所述,Blade 组件现在会有一个与之关联的类来指定接收的数据,定义在组件类中的所有公共属性和方法会自动在组件视图中生效,任何在该组件视图中指定的额外 HTML 属性都可以使用自动引入的 $attribute 变量(它是一个 attribute bag 实例)进行管理。

例如,我们定义一个 App\View\Components\Alert 组件如下:

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    /**
     * The alert type.
     *
     * @var string
     */
    public $type;

    /**
     * Create the component instance.
     *
     * @param  string  $type
     * @return void
     */
    public function __construct($type)
    {
        $this->type = $type;
    }

    /**
     * Get the class for the given alert type.
     *
     * @return string
     */
    public function classForType()
    {
        return $this->type == 'danger' ? 'alert-danger' : 'alert-warning';
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.alert');
    }
}

该组件对应的视图模板代码如下所示:

<!-- /resources/views/components/alert.blade.php -->

<div class="alert {{ $classForType() }}" {{ $attributes }}>
    {{ $heading }}

    {{ $slot }}
</div>

然后我们就可以在另一个 Blade 模板中使用组件标签来渲染该组件:

<x-alert type="error" class="mb-4">
    <x-slot name="heading">
        Alert content...
    </x-slot>

    Default slot content...
</x-alert>

这里只演示了 Blade 组件标签最基本的使用,没有涉及到匿名组件、内联视图组件等特性,你可以参考完整的 Blade 组件文档来了解更多使用明细。

注:之前 Blade 组件的的 @component 语法没有也不会被移除。

HTTP 客户端

作者:Adam WathanJason McCrearyTaylor Otwell

基于 Guzzle HTTP Client,Laravel 现在提供了一个优雅的、最小化的 HTTP 客户端 API,允许你发起 HTTP 请求与其他 Web 应用进行通信。该功能是对 Guzzle 库的封装,并且会专注于自身的使用场景,提供更好的开发者体验。

例如,我们可以通过该功能发起一个 POST 请求,并设定数据传输格式为 JSON,示例代码如下:

use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-First' => 'foo'
    'X-Second' => 'bar'
])->post('http://test.com/users', [
    'name' => 'Taylor',
]);

return $response['id'];

此外,这个 HTTP 客户端还提供了非常棒的、符合人机工程学的测试功能:

Http::fake([
    // Stub a JSON response for GitHub endpoints...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),

    // Stub a string response for Google endpoints...
    'google.com/*' => Http::response('Hello World', 200, ['Headers']),

    // Stub a series of responses for Facebook endpoints...
    'facebook.com/*' => Http::sequence()
                            ->push('Hello World', 200)
                            ->push(['foo' => 'bar'], 200)
                            ->pushStatus(404),
]);

想要了解更多关于 HTTP 客户端的特性,请参考 HTTP 客户端文档

流式字符串操作

作者:Taylor Otwell

如果你之前通过 Laravel 内置的字符串函数处理过字符串的话,可能对 Laravel 中已存在的 Illuminate\Support\Str 类非常熟悉。Laravel 7 现在基于这些函数提供了一个更加面向对象的、更加流式的字符串操作库。你可以使用 String::of 创建一个 Illuminate\Support\Stringable 对象,然后基于该对象提供的方法以方法链的形式对字符串进行处理:

return (string) Str::of('  Laravel Framework 6.x ')
                ->trim()
                ->replace('6.x', '7.x')
                ->slug();

关于平滑字符串操作支持的更多方法信息,请参考完整的官方文档

路由模型绑定优化

作者:Taylor Otwell

自定义键

有时候你可能想要使用 id 之外的其他字段来解析 Eloquent 模型,在 Laravel 7 中,你可以直接在路由参数定义中指定这个字段:

Route::get('api/posts/{post:slug}', function (App\Post $post) {
    return $post;
});

自动化作用域

有时候,当你在单个路由定义中隐式绑定多个 Eloquent 模型时,你可能想要将第二个 Eloquent 模型的作用域设置为第一个 Eloquent 模型的子级。

例如,考虑这个场景 —— 通过 slug 字段为指定用户获取博客文章:

use App\Post;
use App\User;

Route::get('api/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
});

当使用自定义键隐式绑定作为嵌套路由参数时,Laravel 7 会自动将查询作用域设置为获取父级模型的嵌套子模型 —— 使用默认约定规则来猜测父级模型上的关联关系名称。比如这个例子中,就是从指定 User 父级模型(通过 {user} 传入用户ID)上通过关联关系 posts 获取指定 slug 的子级 Post 模型。

想要了解更多关于路由模型绑定的细节,可以参考路由文档

多个邮件驱动

作者:Taylor Otwell

Laravel 7 允许在单个应用中配置多个邮件驱动,mail 配置文件中的每个邮件配置都有自己的选项甚至唯一的 transport,从而允许应用使用不同的邮件服务发送特定邮件消息。例如,你的应用可能使用 Postmark 发送交易邮件,同时使用 Amazon SES 发送群发邮件。

默认情况下,Laravel 会使用 default 配置作为默认邮件配置,不过,你可以在发送邮件时通过 mailer 方法手动指定特定的邮件配置:

Mail::mailer('postmark')
    ->to($request->user())
    ->send(new OrderShipped($order));

路由缓存速度优化

作者:Symfony 贡献者和 Dries Vints

Laravel 7 提供了一个新的方法来匹配那些使用 route:cache 命令缓存的、已编译的缓存路由,在大型应用(例如,超过800个路由)的 "Hello World" 基准测试结果中,这些优化可以将每秒处理请求数提升两倍,不过,该优化对上层代码而言是透明的,无需对现有代码做任何调整。

支持 CORS

作者:Barry vd. Heuvel

Laravel 7 引入了对 CORS(Cross-Origin Resource Sharing,跨域资源共享)的原生支持,这是通过集成 Laravel CORS 扩展包来实现的,你可以在开箱提供的 config/cors.php 配置文件中对其进行配置。

关于 CORS 的更多信息,请参考 CORS 文档

查询时类型转化

作者:Matt Barlow

有时候你可能需要在执行数据库查询时应用字段类型转化,例如从表中查询原生值时。我们来看下面这个例子:

use App\Post;
use App\User;

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->get();

这个查询结果中的 last_posted_at 属性会是一个原生字符串,如果能够在执行查询时应用 date 转化类型到该属性会很方便,这样我们在查询结果中直接得到的就是日期类型。要实现这个目的,可以使用 Laravel 7 新提供的 withCasts 方法:

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->withCasts([
    'last_posted_at' => 'date'
])->get();

MySQL 8+ 数据库队列优化

作者:Mohamed Said

在之前版本的 Laravel 中,由于死锁的原因,基于 database 驱动的队列在生产环境中并不足够稳健。Laravel 7 对此进行了优化,如果应用使用的是 MySQL 8+ 作为数据库队列驱动的话,底层通过使用 FOR UPDATE SKIP LOCKED 和其他 SQL 增强技术,使得 database 驱动现在可以安全地运行在生产环境。

test Artisan 命令

作者:Nuno Maduro

除了 phpunit 命令之外,现在还可以使用 Artisan 命令 test 运行测试。Artisan 测试运行器提供了美观的控制台界面,以及更多关于当前测试结果的信息。此外,运行器会在第一次测试失败时自动终止:

php artisan test

任何传递给 phpunit 命令的参数都可以传递给 test Artisan 命令:

php artisan test --group=feature

Markdown 邮件模板优化

作者:Taylor Otwell

默认的 Markdown 邮件模板基于 Tailwind CSS 调色板进行了全新的、更加现代化的设计。当然,这个模板可以基于应用需求进行发布和自定义:

更多关于 Markdown 邮件的信息,请参考邮件文档

桩自定义

作者:Taylor Otwell

Artisan 控制台的 make 命令可用于创建多种类型的类,比如控制器、队列任务、数据库迁移、以及测试等,这些类都是通过「桩」文件生成的,所谓桩文件可以看作是模板文件,可以基于输入填充模板中指定位置的值最终生成目标 PHP 文件。不过,有时候你可能想要对 Artisan 生成的文件进行一些微调,为此,Laravel 7 提供了 stub.publish 命令将最常用的桩文件发布出来以便进行自定义:

php artisan stub:publish

发布后的桩文件位于项目根目录下的 stubs 目录下,对这些桩文件的任何修改最终都会反馈到通过 Artisan make 命令生成的相应 PHP 代码中。

maxExceptions 队列配置

作者:Mohamed Said

有时候,你可能想要指定一个可能被多次尝试的队列任务,并且在重试次数到达期望值时失败。在 Laravel 7 中,可以在任务类中定义一个 maxExceptions 属性来实现这个功能:

<?php

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 25;

    /**
     * The maximum number of exceptions to allow before failing.
     *
     * @var int
     */
    public $maxExceptions = 3;

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        Redis::throttle('key')->allow(10)->every(60)->then(function () {
            // Lock obtained, process the podcast...
        }, function () {
            // Unable to obtain lock...
            return $this->release(10);
        });
    }
}

在这个例子中,这个任务会在应用无法获取 Redis 锁十秒后释放,然后继续重试至多 25 次,如果任务执行过程中抛出 3 次未处理异常,则会失败退出。

上一篇: 目录索引

下一篇: 升级指南