> ## 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

### 影響程度：中

* 資料庫運算式的變更
* 移除 model 的 `$dates` 屬性
* Monolog 3
* Redis 快取 tag
* Service Mocking 的變更
* 語言目錄

### 影響程度：低

* Closure validation rule 的訊息
* Form Request 的 `after` 方法
* Public path binding
* `QueryException` constructor
* Rate limiter 的回傳值
* 移除 `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)

### 應用程式

#### Public path binding

**影響程度:低**

若你透過將 `path.public` bind 到 container 來自訂 public path，請改為使用 `Illuminate\Foundation\Application` 的 `usePublicPath` 方法。

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

***

### 授權

#### `registerPolicies` 方法

**影響程度:低**

`AuthServiceProvider` 的 `registerPolicies` 方法現在由框架自動呼叫。可從 `boot` 方法中移除此方法的呼叫。

***

### 快取

#### Redis 快取 tag

**影響程度:中**

`Cache::tags()` 現在僅推薦用於使用 Memcached 的應用程式。若使用 Redis 作為快取 driver，請考慮遷移到 Memcached。

***

### 資料庫

#### 資料庫運算式的變更

**影響程度:中**

`DB::raw` 產生的資料庫運算式已被改寫。要取得運算式的原始字串值，必須使用 `getValue(Grammar $grammar)` 方法。無法再使用 `(string)` cast。

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

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

// 變更前
$string = (string) $expression;

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

雖然通常不會影響終端使用者的應用，但若有將資料庫運算式 cast 為字串的情況，需要更新。

#### `QueryException` constructor

**影響程度:非常低**

`Illuminate\Database\QueryException` constructor 的第一個引數新增了字串型 connection name。若有手動 throw 此例外，請修正程式碼。

#### ULID 欄位

**影響程度:低**

migration 中不帶引數呼叫 `ulid` 方法時，欄位名稱會是 `ulid`。之前版本中不帶引數呼叫時，會誤建為名為 `uuid` 的欄位。

```php theme={null}
// 欄位名稱為 "ulid"
$table->ulid();

// 若要明確指定欄位名稱
$table->ulid('ulid');
```

***

### Eloquent

#### Model 的 `$dates` 屬性

**影響程度:中**

Eloquent model 的棄用屬性 `$dates` 已被移除。請使用 `$casts` 屬性。

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

// 變更後
protected $casts = [
    'deployed_at' => 'datetime',
];
```

***

### 語言化

#### 語言目錄

**影響程度:無**

不會影響現有應用程式，但新的 Laravel 應用程式 skeleton 預設不再包含 `lang` 目錄。需要時可用 Artisan 指令發佈。

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

***

### Log

#### Monolog 3

**影響程度:中**

Laravel 的 Monolog 依賴已更新至 Monolog 3.x。若在應用中直接使用 Monolog，請確認 [Monolog 升級指南](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md)。

若使用 BugSnag 或 Rollbar 等第三方 log 服務，可能需要升級到支援 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}
// 變更前
Bus::dispatchNow(new MyJob());
dispatch_now(new MyJob());

// 變更後
Bus::dispatchSync(new MyJob());
dispatch_sync(new MyJob());
```

***

### 路由

#### Middleware alias

**影響程度:選用**

新的 Laravel 應用中，`App\Http\Kernel` 類別的 `$routeMiddleware` 屬性已重新命名為 `$middlewareAliases`。是否套用到既有應用為選用。

#### Rate limiter 的回傳值

**影響程度:低**

呼叫 `RateLimiter::attempt` 方法時，你所提供的 closure 回傳值會被原樣回傳。若沒回傳任何值或回傳 `null`，則會回傳 `true`。

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

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

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

**影響程度:非常低**

已棄用的 `Redirect::home` 方法已被移除。請改為明確地重新導向到命名路由。

```php theme={null}
// 變更前
return Redirect::home();

// 變更後
return Redirect::route('home');
```

***

### 測試

#### Service Mocking

**影響程度:中**

已棄用的 `MocksApplicationServices` trait 已從框架中移除。此 trait 提供了 `expectsEvents`、`expectsJobs`、`expectsNotifications` 等測試方法。

若有使用這些方法，請分別遷移到 `Event::fake`、`Bus::fake`、`Notification::fake`。

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

// 變更後
Event::fake();
// ... 測試程式碼 ...
Event::assertDispatched(OrderShipped::class);
```

***

### 驗證

#### Closure validation rule 的訊息

**影響程度:非常低**

在 closure 型自訂 validation rule 中多次呼叫 `$fail` callback 時，訊息會加到陣列中而不再被覆寫。

另外，`$fail` callback 現在會回傳物件。若對 validation closure 的回傳值有型別標註，請更新。

```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` 方法，請重新命名或變更以善用新的「validation 後」功能。

***

## 總結

Laravel 10 由相對較小的變更組成，升級可在短時間內完成。

| 變更點                           | 影響程度 | 對應                                |
| ----------------------------- | ---- | --------------------------------- |
| 更新 `composer.json` 依賴         | 高    | 改為 `laravel/framework ^10.0`      |
| 必需 PHP 8.1 / Composer 2.2     | 高    | 確認並更新版本                           |
| 資料庫運算式的變更                     | 中    | 將 `(string)` cast 改為 `getValue()` |
| 移除 model 的 `$dates` 屬性        | 中    | 移轉到 `$casts`                      |
| Monolog 3                     | 中    | 若直接使用請確認升級指南                      |
| Redis 快取 tag                  | 中    | 考慮遷移到 Memcached                   |
| 移除 `MocksApplicationServices` | 中    | 移轉到 `Event::fake` 等               |
| 移除 `Bus::dispatchNow`         | 低    | 改為 `Bus::dispatchSync`            |
| ULID 欄位名稱                     | 低    | 確認 migration 的 `ulid()` 呼叫        |
| 移除 `Redirect::home`           | 非常低  | 改為 `Redirect::route('home')`      |

***

## 參考資料

* [官方升級指南（英文）](https://laravel.com/docs/10.x/upgrade)
* [laravel/laravel repository 差異（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-TW/blog/upgrade-10-to-11.md)
- [從 Laravel 8 升級到 9](/zh-TW/blog/upgrade-8-to-9.md)
- [從 Laravel 12 升級到 13 指南](/zh-TW/blog/upgrade-12-to-13.md)
- [從 Laravel 11 升級到 12 指南](/zh-TW/blog/upgrade-11-to-12.md)
