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

# Eloquent Bootable Traits

> Eloquentモデルのトレイト自動起動の仕組みを解説します。`bootXxx()`・`initializeXxx()`メソッドのネーミング規約と、PHP Attributeによる明示的な指定方法を学びます。パッケージ開発でよく使うパターンです。

## 概要

Eloquentモデルはインスタンス化のたびに「起動処理（boot）」と「初期化処理（initialize）」を実行します。トレイトにこれらのメソッドを定義しておくと、モデルがそのトレイトを `use` するだけで自動的に呼び出されます。

この仕組みを使うことで、モデルに機能を注入するトレイトをクリーンに実装できます。Laravelフレームワーク自体も `SoftDeletes` などの多くのトレイトでこのパターンを活用しています。

```mermaid theme={null}
sequenceDiagram
    participant App as アプリケーション
    participant Model as Eloquent Model
    participant Trait as トレイト

    App->>Model: new Post() または Post::find()
    Model->>Model: bootIfNotBooted()
    Model->>Trait: bootXxx() (クラス初回のみ)
    Model->>Model: initializeTraits()
    Model->>Trait: initializeXxx() (インスタンスごと)
    Model-->>App: モデルインスタンス
```

***

## boot と initialize の違い

|               | `bootXxx()`              | `initializeXxx()`   |
| ------------- | ------------------------ | ------------------- |
| **呼び出しタイミング** | モデルクラスの最初の起動時（クラスごとに1回）  | インスタンス生成のたび         |
| **メソッド種別**    | `static`                 | インスタンスメソッド          |
| **用途**        | イベントリスナー登録、グローバルスコープ追加など | デフォルト値設定、プロパティ初期化など |

***

## ネーミング規約

### bootXxx()

トレイト名の末尾に `boot` プレフィックスをつけた **`static`** メソッドを定義すると、モデル起動時に一度だけ呼ばれます。

```php theme={null}
namespace App\Models\Concerns;

trait HasSlug
{
    public static function bootHasSlug(): void
    {
        static::creating(function ($model) {
            if (empty($model->slug)) {
                $model->slug = str($model->name)->slug()->toString();
            }
        });
    }
}
```

モデル側での使用：

```php theme={null}
use App\Models\Concerns\HasSlug;

class Article extends Model
{
    use HasSlug;
    // bootHasSlug() は自動的に呼び出される
}
```

### initializeXxx()

トレイト名の末尾に `initialize` プレフィックスをつけたインスタンスメソッドを定義すると、モデルの `new` のたびに呼ばれます。

```php theme={null}
namespace App\Models\Concerns;

trait HasDefaultStatus
{
    public function initializeHasDefaultStatus(): void
    {
        $this->attributes['status'] ??= 'draft';
    }
}
```

`$casts` への動的な追加にも使えます：

```php theme={null}
trait HasMetadata
{
    public function initializeHasMetadata(): void
    {
        $this->mergeCasts([
            'metadata' => 'array',
        ]);
    }
}
```

***

## PHP Attribute による明示的な指定

Laravel 11 以降、ネーミング規約に頼らず PHP Attribute で明示的にメソッドを指定できます。

| Attribute       | 対象                   |
| --------------- | -------------------- |
| `#[Boot]`       | クラス起動時に一度だけ実行するメソッド  |
| `#[Initialize]` | インスタンス生成のたびに実行するメソッド |

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\Boot;
use Illuminate\Database\Eloquent\Attributes\Initialize;

trait HasAuditLog
{
    #[Boot]
    public static function registerAuditListeners(): void
    {
        static::created(function ($model) {
            AuditLog::record('created', $model);
        });

        static::updated(function ($model) {
            AuditLog::record('updated', $model);
        });
    }

    #[Initialize]
    public function setAuditDefaults(): void
    {
        $this->attributes['audit_enabled'] ??= true;
    }
}
```

<Info>
  PHP Attribute を使うと、メソッド名がトレイト名に縛られないため、意味のある名前をつけられます。複数の `#[Boot]` メソッドや `#[Initialize]` メソッドを同じトレイトに定義することも可能です。
</Info>

***

## 実用例：パッケージ開発での活用

### グローバルスコープの自動追加

```php theme={null}
trait HasTenant
{
    public static function bootHasTenant(): void
    {
        static::addGlobalScope(new TenantScope);
    }
}
```

### デフォルトキャスト追加

```php theme={null}
trait HasJsonSettings
{
    public function initializeHasJsonSettings(): void
    {
        $this->mergeCasts([
            'settings' => 'array',
        ]);
    }
}
```

### Eloquentイベントで自動処理

```php theme={null}
trait Archivable
{
    #[Boot]
    public static function bootArchivable(): void
    {
        static::deleting(function ($model) {
            Archive::store($model->toArray());
        });
    }
}
```

***

## Laravelフレームワーク内の例

Laravelフレームワーク自身も多くのトレイトでこのパターンを採用しています。

| トレイト          | メソッド                | 処理内容                         |
| ------------- | ------------------- | ---------------------------- |
| `SoftDeletes` | `bootSoftDeletes()` | ソフトデリート用グローバルスコープの追加         |
| `HasFactory`  | `bootHasFactory()`  | 特になし（スコープ外の仕組み）              |
| `HasUlids`    | `bootHasUlids()`    | `creating` イベントで ULID を自動セット |
| `HasUuids`    | `bootHasUuids()`    | `creating` イベントで UUID を自動セット |

これらを参考実装として読むと、パターンの理解が深まります。

***

## 注意点

### モデルキャッシュのクリア

`boot*` はクラスごとに一度しか実行されないため、テスト環境では注意が必要です。`Model::clearBootedModels()` で起動済みキャッシュをリセットできます。

```php theme={null}
// テスト後にモデルのブート状態をリセット
protected function tearDown(): void
{
    Model::clearBootedModels();
    parent::tearDown();
}
```

### 実行順序

同一モデルに複数のトレイトが `use` されている場合、`bootXxx()` / `initializeXxx()` はPHPのトレイト解決順（宣言順）で呼ばれます。依存関係がある場合は `use` の順序に注意してください。

***

## まとめ

| パターン      | 方法                                    | 適用場面               |
| --------- | ------------------------------------- | ------------------ |
| クラス起動時処理  | `static bootXxx()` または `#[Boot]`      | イベント登録、グローバルスコープ追加 |
| インスタンス初期化 | `initializeXxx()` または `#[Initialize]` | デフォルト値、キャスト追加      |

トレイトに `bootXxx()` / `initializeXxx()` を実装しておくと、モデルへの機能注入が `use` 一行で済むようになります。パッケージでモデルに機能を追加する際の定番パターンです。


## Related topics

- [Conditionableトレイト](/jp/advanced/conditionable.md)
- [ForwardsCallsトレイト](/jp/advanced/forwards-calls.md)
- [Eloquent入門](/jp/eloquent.md)
- [Eloquent Factories](/jp/eloquent-factories.md)
- [Dumpableトレイト](/jp/advanced/dumpable.md)
