新版特性
新特性概览
声明:Laravel 8 于 2020 年 9 月 8 日正式发布,不是 LTS 版本,所以 bug 修复支持会持续半年,到 2021 年 3 月 8 日,也就是下一个主版本 Laravel 9 发布前后,安全修复则会持续一年,到 2021 年 9 月 8 日。
Laravel 8 在 Laravel 7.x 版本基础上继续进行优化,主要包含了以下更新:
- Laravel Jetstream
- 模型工厂类
- 迁移文件压缩
- 任务批处理
- 访问频率限制优化
- 队列功能优化
- 动态 Blade 组件
- Tailwind 分页视图
- 时间相关的测试辅助函数
artisan serve
命令优化- 事件监听器优化
- 其他 bug 修复和可用性优化
下面我们来简单介绍下上述新增的特性及代码优化:
Laravel Jetstream
作者:Taylor Otwell。
Laravel Jetstream 是一个经过精美设计的 Laravel UI 脚手架,为你的下一个项目提供了非常好的起点,包含了登录、注册、邮箱验证、双因子认证(2FA)、会话管理、基于 Laravel Sanctum 的 API 支持以及可选的团队管理功能。
Laravel Jetstream 是在之前版本的 UI 脚手架基础上进行的裁剪和优化,使用了 Tailwind CSS 框架,在 JavaScript 组件开发上,你可以按照自己的喜好选择 Livewire 或者 Inertia。
模型类目录
应广大社区开发者压倒性的诉求,Laravel 项目默认将包含 app/Models
目录,用于存放 Eloquent 模型类:
所有相关的 Artisan 生成器命令生成的模型类以后都将存放到这个目录。当然,如果这个目录不存在,模型类还是会生成到 app
目录下。
模型工厂类
作者:Taylor Otwell。
Eloquent 模型工厂被重构为基于类进行管理,并且被优化为直接支持关联关系,例如,Laravel 内置的 UserFactory
现在的示例代码如下:
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
}
而由于模型类中包含了 HasFactory
Trait:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
...
因此,可以这样调用模型工厂:
use App\Models\User;
User::factory()->count(50)->create();
由于模型工厂现在是简单的 PHP 类,因此状态转换可以通过类方法的方式实现。此外,你还可以按照需要添加任意其它的辅助类到 Eloquent 模型工厂。
例如,User
模型可能有一个 suspended
状态用于修改某个默认属性值(状态方法名可以随便取),你可以使用工厂基类提供的 state
方法来定义状态转换,最后呈现出来的就是一个典型的 PHP 方法:
/**
* Indicate that the user is suspended.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function suspended()
{
return $this->state([
'account_status' => 'suspended',
]);
}
定义好状态转换方法后,我们可以这样使用它:
use App\Models\User;
User::factory()->count(5)->suspended()->create();
正如前面所提到的,Laravel 8 的模型工厂包含对关联关系的一等支持,所以,假设 User
模型包含 posts
关联方法,我们可以简单运行如下代码来生成一个拥有三篇文章的用户:
$users = User::factory()
->hasPosts(3, [
'published' => false,
])
->create();
为了简化升级流程, Laravel 官方还专门发布了 laravel/legacy-factories 包用于在 Laravel 8.x 中兼容之前版本的模型工厂。
Laravel 对模型工厂的重构还包含了很多你可能会喜欢的其它新特性,更多细节,请参考数据库测试文档。
迁移文件压缩
作者:Taylor Otwell。
在构建 Laravel 应用的过程中,随着时间的推移,可能会累积越来越多的数据库迁移文件,这可能会导致迁移目录越来越臃肿,也变得难以管理(比如上百个迁移文件),Laravel 8 引入了将多个迁移文件压缩到单个 SQL 文件的机制来解决这个问题。只需要执行 schema:dump
命令即可:
php artisan schema:dump
// Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune
当你执行这个命令时,Laravel 会创建一个表结构文件到 database/schema
目录下:
这样一来,执行数据库迁移命令将不再执行迁移文件,而是执行表结构文件中的 SQL 语句。执行完这些 SQL 语句后,如果还有未压缩的迁移文件,再去执行它们。
任务批处理
作者:Taylor Otwell 和 Mohamed Said。
Laravel 的任务批处理功能允许你轻松运行批量任务,在这些批量任务运行完毕后还可以执行指定动作。
你可以使用 Bus
门面新增的 batch
方法来分发批量任务。当然,批处理只有和完成回调结合起来才最有用,你可以使用 then
、catch
和 finally
方法为批处理定义完成回调,每个回调函数在被调用时都会接收一个 Illuminate\Bus\Batch
实例:
use App\Jobs\ProcessPodcast;
use App\Podcast;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Batch;
use Throwable;
$batch = Bus::batch([
new ProcessPodcast(Podcast::find(1)),
new ProcessPodcast(Podcast::find(2)),
new ProcessPodcast(Podcast::find(3)),
new ProcessPodcast(Podcast::find(4)),
new ProcessPodcast(Podcast::find(5)),
])->then(function (Batch $batch) {
// 所有任务执行成功...
})->catch(function (Batch $batch, Throwable $e) {
// 第一个失败的任务...
})->finally(function (Batch $batch) {
// 批处理任务执行完毕...
})->dispatch();
return $batch->id;
想要了解更多任务批处理的使用,可以参考队列文档。
优化访问频率限制
作者:Taylor Otwell。
Laravel 的请求频率限制功能在本次新版本中得到了增强,变得更加灵活,功能也更加强大,同时保持了对之前版本 throttle
中间件的向后兼容。
访问频率限制器可以使用 RateLimiter
门面的 for
方法进行定义。该方法接收一个频率限制器名称以及一个返回限制配置(配置会应用到频率限制器分配到的路由上)的闭包作为参数:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('global', function (Request $request) {
return Limit::perMinute(1000);
});
由于访问频率限制器回调函数接收的是 HTTP 请求实例,因此你可以基于这个输入请求或者认证用户动态设置相应的访问频率限制:
RateLimiter::for('uploads', function (Request $request) {
return $request->user()->vipCustomer()
? Limit::none()
: Limit::perMinute(100);
});
有时候,你可能想要使用多个字段值来定义访问频率限制,例如,你可能想要设置指定 IP 用户每分钟访问给定路由不超过 100 次,要实现这个功能,你可以在构建访问频率配置时使用 use
方法:
RateLimiter::for('uploads', function (Request $request) {
return $request->user()->vipCustomer()
? Limit::none()
: Limit::perMinute(100)->by($request->ip());
});
访问频率限制器可以通过 throttle
中间件分配给指定路由或者路由群组,只需要将限制器的名称通过参数传递给 throttle
中间件即可:
Route::middleware(['throttle:uploads'])->group(function () {
Route::post('/audio', function () {
//
});
Route::post('/video', function () {
//
});
});
想要了解更多关于访问频率限制的细节,请参考路由文档。
优化维护模式
作者:Taylor Otwell(灵感来自 Spatie)。
在之前版本的 Laravel 中,通过 php artisan down
命令进入维护模式这一功能可以通过定义允许访问应用的 IP 地址「白名单」绕过,该特性在 Laravel 8 中已经被移除,取而代之的是更加简单的密钥/令牌解决方案。
将系统设置为维护模式的时候,你可以使用 secret
选项指定绕过维护模式的令牌:
php artisan down --secret="1630542a-246b-4b66-afa1-dd72a4c43515"
这样一来,应用处于维护模式后,仍然可以通过带有上述令牌的 URL 访问应用,Laravel 会为其颁发一个绕过维护模式的 Cookie:
https://example.com/1630542a-246b-4b66-afa1-dd72a4c43515
访问上面这个 URL 后会重定向到 /
路由,此时,由于浏览器中已经包含绕开维护模式的令牌,所以你可以像正常模式一样访问应用所有路由,就好像并没有启动维护模式一样。
预渲染维护模式视图
如果你在开发期间已经使用 php artisan down
命令进入维护模式,用户仍然可能偶尔遇到错误 —— 如果访问应用的同时 Composer 依赖或者其他基础设施组件正在更新。这种情况会发生是因为 Laravel 框架的基础组成部分必须启动后才能才能判断应用是否处于维护模式,然后使用模板引擎渲染维护模式视图。
为此,Laravel 现在允许你预渲染维护模式视图,它会在请求生命周期的早期返回,这样一来,该视图就会在所有应用依赖加载前被渲染。你可以使用 down
命令的 render
选项选择要预渲染的视图模板:
php artisan down --render="errors::503"
闭包分发和链式 catch
作者:Mohamed Said
你现在可以使用新的 catch
方法定义一个闭包,该闭包会在为队列任务配置的所有尝试次数用完之后仍然失败的情况下运行:
use Throwable;
dispatch(function () use ($podcast) {
$podcast->publish();
})->catch(function (Throwable $e) {
// This job has failed...
});
动态 Blade 组件
作者:Taylor Otwell。
有时候你可能需要渲染一个组件,但是这个组件只有到运行时才能确定,在这种情况下,你可以使用 Laravel 内置的 dynamic-component
组件基于运行时的值或者变量渲染特定组件:
<x-dynamic-component :component="$componentName" class="mt-4" />
想要了解更多关于 Blade 组件的细节,请参考 Blade 文档。
事件监听器优化
作者:Taylor Otwell。
现在只能通过将闭包传递给 Event::listen
方法来注册基于闭包的事件监听器。Laravel 会通过这个闭包来判断监听器处理器的事件类型:
use App\Events\PodcastProcessed;
use Illuminate\Support\Facades\Event;
Event::listen(function (PodcastProcessed $event) {
//
});
此外,基于闭包的事件监听器现在需要使用 Illuminate\Events\queueable
函数标记为通过队列进行处理:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
use Illuminate\Support\Facades\Event;
Event::listen(queueable(function (PodcastProcessed $event) {
//
}));
和队列任务一样,你可以使用 onConnection
、onQueue
和 delay
方法来自定义队列化事件监听器的执行:
Event::listen(queueable(function (PodcastProcessed $event) {
//
})->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10)));
如果你想要捕获异步队列监听器的失败,可以在定义 queueable
监听器时提供一个闭包到 catch
方法进行处理即可:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
use Illuminate\Support\Facades\Event;
use Throwable;
Event::listen(queueable(function (PodcastProcessed $event) {
//
})->catch(function (PodcastProcessed $event, Throwable $e) {
// 队列监听器处理失败...
}));
时间测试辅助函数
作者:Taylor Otwell(灵感源自 Ruby on Rails)。
进行代码测试时,你可能偶尔需要修改 now()
或者 Illuminate\Support\Carbon::now()
函数返回的时间,Laravel 功能测试基类现在内置了相关的辅助函数,你可以使用它们来操纵当前时间:
public function testTimeCanBeManipulated()
{
// 穿越到未来...
$this->travel(5)->milliseconds();
$this->travel(5)->seconds();
$this->travel(5)->minutes();
$this->travel(5)->hours();
$this->travel(5)->days();
$this->travel(5)->weeks();
$this->travel(5)->years();
// 穿越到过去...
$this->travel(-5)->hours();
// 穿越到指定时间...
$this->travelTo(now()->subHours(6));
// 回到现在...
$this->travelBack();
}
Artisan serve
命令优化
作者:Taylor Otwell。
从 Laravel 8 开始,当本地 .env
文件中的环境变量修改后,Artisan serve
命令会自动进行服务器重载,以便让对应配置修改生效。在此之前,需要手动停止并重启。
Tailwind 分页视图
Laravel 分页器现在被更新为默认使用 Tailwind CSS 框架,Tailwind CSS 是一个高度可定制的基础层 CSS 框架,它为开发者提供了构建定制化设计所需的所有构建区块,而无需重新覆盖任何内建于框架中的设计风格。当然,Bootstrap 3 和 4 对应的分页视图也依然保留,你可以自行选择想要使用的分页风格。
路由控制器命名空间更新
在之前版本的 Laravel 中,RouteServiceProvider
包含一个 $namespace
属性:
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
...
该属性的值会被自动应用到定义在路由中的、调用 action
辅助函数或者 URL::action
方法时的控制器命名空间前缀。在 Laravel 8.x 中,移除了属性,这意味着控制器不会再有自动设置的命名空间前缀,因此,在 Laravel 8 项目中,控制器路由定义需要使用标准的 PHP 调用语法:
use App\Http\Controllers\UserController;
Route::get('/users', [UserController::class, 'index']);
调用 action
相关的方法也需要使用同样的调用语法:
action([UserController::class, 'index']);
return Redirect::action([UserController::class, 'index'])
如果你更喜欢 Laravel 7.x 风格的控制器路由前缀,在 RouteServiceProvider
中设置 $namespace
属性值即可。
注:这个更改只影响新创建的 Laravel 8.x 应用,从 7.x 版本升级过来的应用仍然会在
RouteServiceProvider
中保留$namespace
属性。
6 条评论
路由这块是最大的坑,几百个路由一个个人工改
看文档不看完?最后一段不是写了之前版本升级过来的不用调整吗?!
不,我不知道他们改了什么 这种格式的他不支持的 Route::group(array('namespace' => 'Blog'), function () { Route::get('/', 'IndexController@index')->name('index');
}); 这种不完整的方式的,官方并没有兼容,需要我们自己一个个改的! protected $namespace = 'App\Http\Controllers'; 这个属性他根本就没合并上去,他们少了
你从低版本升级到8的时候不去动 RouteServiceProvider 不就可以了?
Laravel 9 咋没发布
知识星球里面说过了,Laravel 后面将调整为一年发布一个新版本,Laravel 9 会在今年 9 月份发布