新版特性
声明: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
接口的类来完成这个自定义。
实现该接口的类必须定义 get
和 set
方法,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 组件标签 & 优化
作者:Spatie、Marcel Pociot、Caleb Porzio、Dries Vints 和 Taylor 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 Wathan、Jason McCreary 和 Taylor 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 次未处理异常,则会失败退出。
No Comments