
# ワークフローのキャンセル

ワークフローはプログラムまたはCLI経由でキャンセルできます。ワークフローをキャンセルすると、Queuetyはそのステータスを`cancelled`に設定し、保留中または処理中のジョブをすべて保留状態に移し、任意でクリーンアップハンドラーを実行します。

## クリーンアップハンドラーの登録

ワークフロービルダーで`on_cancel()`を使い、ワークフローのキャンセル時に実行するクラスを登録します。ハンドラークラスは`handle( array $state ): void`メソッドを実装する必要があります。

```php
use Queuety\Queuety;

Queuety::workflow( 'import_data' )
    ->then( FetchDataHandler::class )
    ->then( TransformDataHandler::class )
    ->then( LoadDataHandler::class )
    ->on_cancel( ImportCleanupHandler::class )
    ->dispatch( [ 'source_url' => $url ] );
```

クリーンアップハンドラーには、ワークフローの公開状態（`_`で始まらないすべてのキー）が渡されます。

```php
class ImportCleanupHandler {

    public function handle( array $state ): void {
        // Clean up any partial imports.
        if ( isset( $state['temp_table'] ) ) {
            global $wpdb;
            $wpdb->query( "DROP TABLE IF EXISTS {$state['temp_table']}" );
        }

        // Notify the user.
        wp_mail(
            $state['notify_email'] ?? '',
            'Import cancelled',
            'Your data import was cancelled.'
        );
    }
}
```

## PHP経由でのキャンセル

ワークフローIDを指定して`Queuety::cancel_workflow()`を呼び出します。

```php
Queuety::cancel_workflow( $workflow_id );
```

このメソッドは次の処理を行います。

1. ワークフローの行をロックする
2. ワークフローのステータスを`cancelled`に設定する
3. 「Workflow cancelled」というメッセージを付けて、そのワークフローの保留中および処理中のジョブをすべて保留状態に移す
4. `workflow_cancelled`イベントを記録してコミットする
5. 完了済みステップの補償処理を逆順に実行する（`compensate_with()`経由で登録されている場合）
6. `on_cancel`クリーンアップハンドラーを実行する（登録されている場合）

ワークフローがすでに`completed`または`cancelled`の場合、`RuntimeException`がスローされます。

## CLI経由でのキャンセル

```bash
wp queuety workflow cancel <id>
```

| オプション | 説明 |
|---|---|
| `<id>` | キャンセルするワークフローID（必須） |

```bash
wp queuety workflow cancel 42
```

## キャンセル時の動作

ワークフローをキャンセルすると、次の処理が行われます。

- **ステータス**が`cancelled`に変わる
- **ステップの補償処理**は、`compensate_with()`で設定している場合、完了した順序の逆順に実行される
- **クリーンアップハンドラー**は、ワークフローの行が`cancelled`に設定された後に実行される
- **保留中のジョブ**は直ちに保留状態に移される
- **処理中のジョブ**は保留状態に移される。実行中のハンドラーが終了すると、その出力は破棄され、ワークフローは次に進まない
- **完了済みのジョブ**はそのまま残る
- **ログエントリー**にイベント`workflow_cancelled`が記録される

## キャンセルと失敗の違い

| 項目 | 失敗 | キャンセル |
|---|---|---|
| トリガー | ステップの再試行回数を使い切る | `cancel_workflow()`の明示的な呼び出し |
| ステータス | `failed` | `cancelled` |
| 再試行可能 | はい。ワークフローの補償処理がまだ行われていない場合は`retry_workflow()`経由で可能 | いいえ |
| クリーンアップハンドラー | 呼び出されない | `on_cancel()`経由で登録されている場合に呼び出される |
| ログイベント | `workflow_failed` | `workflow_cancelled` |

## 例：確認してからキャンセルする

よくあるパターンは、キャンセル前にワークフローのステータスを確認することです。

```php
$status = Queuety::workflow_status( $workflow_id );

if ( null === $status ) {
    throw new \RuntimeException( 'Workflow not found.' );
}

if ( 'running' === $status->status->value || 'paused' === $status->status->value ) {
    Queuety::cancel_workflow( $workflow_id );
}
```
