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

# 套件快取與 optimize 的整合

> 使用 Laravel 13 的 optimizes()，將套件專屬快取的產生與刪除整合進部署流程。從實作解說註冊鍵、排除指定、執行順序，以及失敗時的結束代碼。

當套件需要預先產生自己的中繼資料時，若只是請使用者在部署步驟中加入專用指令，更新時很容易漏掉執行。使用 `ServiceProvider::optimizes()`，就能將產生與刪除的指令整合進 Laravel 的 `optimize` 與 `optimize:clear`。

本頁以[套件開發的基礎](/zh-TW/advanced/package-development)為前提，確認 Laravel Framework `v13.35.0` 的實作。這裡探討的不是快取檔案的格式，而是註冊與維運上的約定。

## 將指令的註冊與任務的註冊分開

`commands()` 會註冊可從 Artisan 呼叫的指令類別。`optimizes()` 則是另一項處理，將已可執行的指令名稱註冊為最佳化任務。只呼叫後者的話，指令類別並不會被註冊。

下列範例的前提是，套件已實作 `CacheMetadataCommand` 與 `ClearMetadataCommand`，且兩者的 `$signature` 分別為 `courier:cache` 與 `courier:clear-cache`。

```php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Console\Commands\CacheMetadataCommand;
use Acme\Courier\Console\Commands\ClearMetadataCommand;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                CacheMetadataCommand::class,
                ClearMetadataCommand::class,
            ]);

            $this->optimizes(
                optimize: 'courier:cache',
                clear: 'courier:clear-cache',
                key: 'acme-courier',
            );
        }
    }
}
```

`optimizes()` 的參數全都可為 null，因此也可以只註冊產生或只註冊刪除。不過，請務必告知使用者要透過哪個步驟讓產生的快取失效。

## 註冊鍵也會成為面向使用者的約定

`ServiceProvider` 會將產生指令儲存在靜態陣列 `$optimizeCommands`，將刪除指令儲存在 `$optimizeClearCommands`。兩者都以 `key` 作為陣列鍵。

省略 `key` 時，會從提供者的類別名稱產生名稱。例如 `CourierServiceProvider` 會得到 `courier`。由於只使用類別名稱，即使是位於不同命名空間的同名提供者也可能發生衝突。

以相同的鍵重新註冊時，該側的指令會被後來的值覆寫。若要註冊多個任務，請指定不同的鍵。此外，請避開 `config` 或 `routes` 等 Laravel 標準任務的鍵，因為在合併標準任務與套件任務時，相同的字串鍵也會被覆寫。

<Tip>
  請像 `acme-courier` 這樣明確指定能識別套件的鍵，並在各版本之間維持不變。鍵會成為任務的顯示名稱，也是使用者以 `--except` 指定的值。
</Tip>

## 在標準任務之後執行

在確認的實作中，兩個指令都會先將套件的註冊陣列展開到標準任務的陣列中，再依序呼叫。使用不衝突的鍵時，套件任務會被加在標準任務之後。

| 指令 | 標準任務的執行順序 | 之後的處理 |
| - | - | - |
| `optimize` | `config:cache` → `event:cache` → `route:cache` → `view:cache` | 已註冊的產生指令 |
| `optimize:clear` | `config:clear` → `cache:clear` → `clear-compiled` → `event:clear` → `route:clear` → `view:clear` | 已註冊的刪除指令 |

```mermaid theme={null}
flowchart TD
    A["提供者的 boot()"] --> B["以 commands() 註冊 Artisan 指令"]
    A --> C["以 optimizes() 註冊鍵與指令名稱"]
    B --> D["php artisan optimize"]
    C --> D
    D --> E["快取標準的設定、事件、路由與視圖"]
    E --> F["執行 courier:cache"]
    C --> G["php artisan optimize:clear"]
    G --> H["刪除標準快取"]
    H --> I["執行 courier:clear-cache"]
```

基於這個順序，產生指令不應重建其他標準快取，而只產生套件所擁有的資料。請不要把 `optimizes()` 當作控制多個套件之間相依順序的 API；若需要嚴格的順序，請明確排列專用指令。

<Warning>
  `optimize:clear` 也包含 `cache:clear`，會一併刪除預設快取儲存區的資料。若只想清除套件專用的快取，請直接執行 `courier:clear-cache`。套件的刪除指令本身也應設計成不 flush 整個共用儲存區，只刪除自己擁有的鍵或檔案。
</Warning>

## 以鍵或指令名稱排除

兩個指令的 `--except` 都接受以逗號分隔的值。系統會去除每個值前後的空白，並排除與任務鍵或指令名稱相符的項目。

```bash theme={null}
php artisan optimize --except=acme-courier
php artisan optimize --except=courier:cache
php artisan optimize:clear --except=acme-courier,cache
```

前兩行排除的是同一個產生任務。第三行則排除套件的刪除任務，以及標準的 `cache:clear`。`cache` 是任務的鍵，並不是套件專用的名稱。

排除指定只對該次執行有效。它不是用來停用提供者的註冊，或自動刪除先前建立之套件快取的設定。

## 區分任務的 FAIL 與父指令的結束代碼

`OptimizeCommand` 與 `OptimizeClearCommand` 會以 `callSilently()` 呼叫各任務，並將結束代碼是否為 `0` 傳給任務顯示。由於一般子指令的輸出不會顯示，調查原因時請直接執行專用指令。

在 Laravel `v13.35.0` 中，兩者的 `handle()` 即使子指令回傳非零值，也不會將該值作為父指令的回傳值。即使畫面上顯示 `FAIL`，迴圈仍會繼續，若沒有例外，父指令的結束代碼就會是 `0`。另一方面，拋出的例外會在任務顯示元件中重新拋出，因此不會有相同的繼續執行行為。

<Warning>
  請不要僅憑 `php artisan optimize` 的結束代碼為 `0`，就判斷套件快取已成功產生。此行為是基於所確認版本的實作，因此在更新支援的 Laravel 版本時也請重新確認。
</Warning>

若套件的產生是部署的必要條件，請採用能直接確認子指令結束代碼的步驟。例如，下列範例將套件任務從批次執行中排除，並在標準任務之後直接執行一次。

```bash theme={null}
php artisan optimize --except=acme-courier && php artisan courier:cache
```

此範例會將 `courier:cache` 的失敗反映在結束代碼上，但並不會彙整標準任務的非零結束。若部署也需要嚴格偵測標準任務，請個別執行所需的指令並確認其結束代碼。

## 經得起更新的快取設計與確認

除了註冊之外，也要事先決定指令與讀取端的職責。

* 產生即使重複執行，也應從相同輸入得到相同狀態，且中途失敗時不應讓不完整的資料生效。
* 刪除在快取不存在時也應正常完成，且不刪除使用者已公開的設定或永久資料。
* 產生失敗的指令應回報錯誤並回傳非零值。讀取端也不應無條件將損壞的快取視為正常。
* 變更快取格式時，請告知使用者需要重新產生，並考慮重新啟動長時間執行的程序。

除了套件的測試之外，請在使用端應用程式中確認下列項目。由於註冊陣列是靜態的，也請留意同一程序內各測試之間註冊狀態的殘留。

| 確認的操作 | 完成條件 |
| - | - |
| 直接執行專用的產生與刪除指令 | 正常時為 `0`，產生失敗時為非零。執行兩次刪除也會成功 |
| 執行 `optimize` / `optimize:clear` | 套件任務各被呼叫一次，產生與刪除後的狀態正確 |
| 以鍵與指令名稱指定 `--except` | 只有目標任務不會被執行 |
| 讓產生指令以非零結束 | 能區分 `FAIL` 顯示與所確認版本中父指令的結束代碼 |
| 套件更新後重新產生 | 以新的程式碼與設定產生，不會繼續讀取舊格式 |

## 相關頁面

<Columns cols={2}>
  <Card title="套件設定的合併與快取" icon="sliders" href="/zh-TW/advanced/package-config-merging">
    確認已公開設定的補全與設定快取重建之間的關係。
  </Card>

  <Card title="以 Orchestra Testbench 測試 Laravel 套件" icon="flask" href="/zh-TW/advanced/package-testing">
    在測試環境中註冊提供者與 Artisan 指令。
  </Card>
</Columns>

## 參考的一手資料

* [Laravel 官方文件：Optimize commands](https://github.com/laravel/docs/blob/13.x/packages.md#optimize-commands)
* [ServiceProvider：optimizes() 與註冊鍵](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [OptimizeCommand：任務與排除處理](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeCommand.php)
* [OptimizeClearCommand：刪除任務與執行順序](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeClearCommand.php)
* [Command：handle() 的回傳值與結束代碼](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Task：結果顯示與例外的重新拋出](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/View/Components/Task.php)


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [快取](/zh-TW/cache.md)
- [套件自動偵測的內部結構](/zh-TW/advanced/package-discovery.md)
- [引擎 API 整合應用開發指南 - VOICEVOX for Laravel](/zh-TW/packages/laravel-voicevox/app-guide.md)
- [Laravel Socialite（社群認證）](/zh-TW/socialite.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.