> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel 9 升级到 10

> Laravel 9.x 到 10.x 的升级步骤与主要变更点解析。

## 前言

Laravel 10 于 2023 年 2 月 14 日发布。本指南讲解从 Laravel 9.x 升级到 10.x 的步骤。

<Info>
  预计升级时间**约 10 分钟**。当然,破坏性变更对应用的影响会因规模和所用功能而异。
</Info>

### 用 Laravel Shift 自动升级

也可以用 [Laravel Shift](https://laravelshift.com/) 自动化升级。Shift 会自动更新应用的依赖和配置文件。

***

## 按影响面分级的变更

### 影响度:高

* 依赖更新
* 更新 minimum-stability

### 影响度:中

* 数据库 Expression 变更
* 移除模型的 `$dates` 属性
* Monolog 3
* Redis 缓存 tags
* 服务 mock 变更
* 语言目录

### 影响度:低

* 闭包校验规则的消息
* Form Request 的 `after` 方法
* 公共路径绑定
* `QueryException` 构造函数
* 限流器的返回值
* 移除 `Redirect::home` 方法
* 移除 `Bus::dispatchNow` 方法
* `registerPolicies` 方法
* ULID 列

***

## 升级步骤

### 更新依赖

**影响度:高**

请更新 `composer.json` 中的以下依赖。

```json theme={null}
{
  "require": {
    "laravel/framework": "^10.0",
    "laravel/sanctum": "^3.2",
    "doctrine/dbal": "^3.0",
    "spatie/laravel-ignition": "^2.0"
  }
}
```

如果用了以下包,请一并升级。

```json theme={null}
{
  "require": {
    "laravel/passport": "^11.0",
    "laravel/ui": "^4.0"
  }
}
```

使用 PHPUnit 10 时也请更新:

```json theme={null}
{
  "require-dev": {
    "nunomaduro/collision": "^7.0",
    "phpunit/phpunit": "^10.0"
  }
}
```

<Warning>
  使用 PHPUnit 10 时,请从 `phpunit.xml` 的 `<coverage>` 部分删除 `processUncoveredFiles` 属性。
</Warning>

之后执行:

```shell theme={null}
composer update
```

从 Sanctum 2.x 升到 3.x,请另外查阅 [Sanctum 3.x 升级指南](https://github.com/laravel/sanctum/blob/3.x/UPGRADE.md)。

***

### 更新 minimum-stability

**影响度:高**

将 `composer.json` 的 `minimum-stability` 更新为 `stable`。因为默认值就是 `stable`,你也可以直接删掉这项。

```json theme={null}
"minimum-stability": "stable"
```

***

## PHP 版本要求

**影响度:高**

Laravel 10 需要 **PHP 8.1.0 及以上** 与 **Composer 2.2.0 及以上**。

***

## 破坏性变更(Breaking Changes)

### 应用

#### 公共路径绑定

**影响度:低**

如果你通过绑定 `path.public` 来自定义公共路径,请改用 `Illuminate\Foundation\Application` 的 `usePublicPath` 方法。

```php theme={null}
app()->usePublicPath(__DIR__.'/public');
```

***

### 授权

#### `registerPolicies` 方法

**影响度:低**

`AuthServiceProvider` 的 `registerPolicies` 方法现在由框架自动调用。可以从 `boot` 方法中删除对它的调用。

***

### 缓存

#### Redis 缓存 tags

**影响度:中**

`Cache::tags()` 仅建议在使用 Memcached 的应用中使用。使用 Redis 作为缓存驱动时,建议迁移到 Memcached。

***

### 数据库

#### 数据库 Expression 变更

**影响度:中**

`DB::raw` 生成的数据库 Expression 被重写。要拿到 Expression 的原始字符串值,需要用 `getValue(Grammar $grammar)` 方法。不能再用 `(string)` 强转。

```php theme={null}
use Illuminate\Support\Facades\DB;

$expression = DB::raw('select 1');

// Before
$string = (string) $expression;

// After
$string = $expression->getValue(DB::connection()->getQueryGrammar());
```

普通终端应用一般不会受到影响,但只要你有对数据库 Expression 做字符串强转,就需要修改。

#### `QueryException` 构造函数

**影响度:非常低**

`Illuminate\Database\QueryException` 的构造函数第一个参数增加了字符串类型的连接名。如果你手动抛这个异常,需要相应修改。

#### ULID 列

**影响度:低**

在迁移里不带参数调用 `ulid` 方法时,列名会是 `ulid`。以前不带参数调用会错误地创建名为 `uuid` 的列。

```php theme={null}
// 列名会是 "ulid"
$table->ulid();

// 明确指定列名
$table->ulid('ulid');
```

***

### Eloquent

#### 模型的 `$dates` 属性

**影响度:中**

Eloquent 模型上已废弃的 `$dates` 属性被移除。请改用 `$casts`。

```php theme={null}
// Before
protected $dates = ['deployed_at'];

// After
protected $casts = [
    'deployed_at' => 'datetime',
];
```

***

### 本地化

#### 语言目录

**影响度:无**

对已有应用没有影响,但新的 Laravel 应用骨架默认不再包含 `lang` 目录。需要时可以用 Artisan 命令发布。

```shell theme={null}
php artisan lang:publish
```

***

### 日志

#### Monolog 3

**影响度:中**

Laravel 对 Monolog 的依赖升到了 Monolog 3.x。如果你在应用里直接使用 Monolog,请查阅 [Monolog 的升级指南](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md)。

使用 BugSnag、Rollbar 等第三方日志服务时,可能需要升级到支持 Monolog 3.x 与 Laravel 10.x 的版本。

<Tip>
  Monolog 3.x 的变更点可以在 [Monolog 3.x 升级指南](https://github.com/Seldaek/monolog/blob/3.x/UPGRADE.md) 中查看。
</Tip>

***

### 队列

#### 移除 `Bus::dispatchNow` 方法

**影响度:低**

已废弃的 `Bus::dispatchNow` 和 `dispatch_now` 方法被移除。请使用 `Bus::dispatchSync` 和 `dispatch_sync`。

```php theme={null}
// Before
Bus::dispatchNow(new MyJob());
dispatch_now(new MyJob());

// After
Bus::dispatchSync(new MyJob());
dispatch_sync(new MyJob());
```

***

### 路由

#### 中间件别名

**影响度:任选**

新的 Laravel 应用里,`App\Http\Kernel` 类的 `$routeMiddleware` 属性改名为 `$middlewareAliases`。要不要在已有应用里跟着改是你的自由。

#### 限流器的返回值

**影响度:低**

调用 `RateLimiter::attempt` 时,你提供的闭包的返回值会被原样返回。什么都不返回或返回 `null` 时,会返回 `true`。

```php theme={null}
$value = RateLimiter::attempt('key', 10, fn () => ['example'], 1);

$value; // ['example']
```

#### 移除 `Redirect::home` 方法

**影响度:非常低**

已废弃的 `Redirect::home` 方法被移除。请显式改为命名路由跳转。

```php theme={null}
// Before
return Redirect::home();

// After
return Redirect::route('home');
```

***

### 测试

#### 服务 mock

**影响度:中**

已废弃的 `MocksApplicationServices` trait 从框架中移除。它曾经提供 `expectsEvents`、`expectsJobs`、`expectsNotifications` 等测试方法。

如果你还在用这些方法,请分别迁移到 `Event::fake`、`Bus::fake`、`Notification::fake`。

```php theme={null}
// Before
$this->expectsEvents(OrderShipped::class);

// After
Event::fake();
// ... 测试代码 ...
Event::assertDispatched(OrderShipped::class);
```

***

### 校验

#### 闭包校验规则的消息

**影响度:非常低**

在基于闭包的自定义校验规则里多次调用 `$fail` 回调时,消息不再是被覆盖,而是追加到数组中。

同时,`$fail` 回调现在返回对象。如果你为校验闭包的返回值加了类型提示,需要相应修改。

```php theme={null}
public function rules()
{
    return [
        'name' => [
            function ($attribute, $value, $fail) {
                $fail('validation.translation.key')->translate();
            },
        ],
    ];
}
```

#### Form Request 的 `after` 方法

**影响度:非常低**

Form Request 里的 `after` 方法名被 Laravel 保留。如果你在 Form Request 上定义了 `after` 方法,请重命名或改造,让它用上新的“校验后”机制。

***

## 总结

Laravel 10 的变更相对小,可以在很短时间内完成升级。

| 变更点                           | 影响度 | 应对                             |
| ----------------------------- | --- | ------------------------------ |
| `composer.json` 依赖更新          | 高   | 改为 `laravel/framework ^10.0`   |
| 必须 PHP 8.1 / Composer 2.2     | 高   | 检查并更新版本                        |
| 数据库 Expression 变更             | 中   | 把 `(string)` 强转改为 `getValue()` |
| 移除模型 `$dates` 属性              | 中   | 迁移到 `$casts`                   |
| Monolog 3                     | 中   | 直接使用时请查阅升级指南                   |
| Redis 缓存 tags                 | 中   | 考虑迁移到 Memcached                |
| 移除 `MocksApplicationServices` | 中   | 迁移到 `Event::fake` 等            |
| 移除 `Bus::dispatchNow`         | 低   | 改为 `Bus::dispatchSync`         |
| ULID 列名                       | 低   | 检查迁移中的 `ulid()` 调用             |
| 移除 `Redirect::home`           | 非常低 | 改为 `Redirect::route('home')`   |

***

## 参考资料

* [官方升级指南(英文)](https://laravel.com/docs/10.x/upgrade)
* [laravel/laravel 仓库差异(9.x → 10.x)](https://github.com/laravel/laravel/compare/9.x...10.x)
* [Laravel Shift](https://laravelshift.com) —— 自动化升级的社区服务
* [Sanctum 升级指南(2.x → 3.x)](https://github.com/laravel/sanctum/blob/3.x/UPGRADE.md)
* [Monolog 3.x 升级指南](https://github.com/Seldaek/monolog/blob/3.x/UPGRADE.md)


## Related topics

- [Laravel 10 升级到 11](/zh/blog/upgrade-10-to-11.md)
- [Laravel 12 升级到 13 指南](/zh/blog/upgrade-12-to-13.md)
- [Laravel 8 升级到 9](/zh/blog/upgrade-8-to-9.md)
- [Laravel 11 升级到 12 指南](/zh/blog/upgrade-11-to-12.md)
