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

> Explicación del mecanismo de arranque automático de traits en modelos Eloquent. Aprende las convenciones de nomenclatura de los métodos `bootXxx()` e `initializeXxx()`, además de cómo indicarlos explícitamente mediante PHP Attributes. Un patrón habitual en el desarrollo de paquetes.

## Resumen

Cada vez que se instancia un modelo Eloquent se ejecutan dos fases: el "arranque" (boot) y la "inicialización" (initialize). Si defines determinados métodos en un trait, basta con que el modelo lo declare mediante `use` para que Eloquent los descubra y los invoque automáticamente.

Gracias a este mecanismo puedes implementar traits que inyectan funcionalidad en tus modelos de forma limpia. El propio framework Laravel aprovecha este patrón en muchos traits como `SoftDeletes`, `HasUuids` o `HasUlids`.

```mermaid theme={null}
sequenceDiagram
    participant App as Aplicación
    participant Model as Eloquent Model
    participant Trait as Trait

    App->>Model: new Post() o Post::find()
    Model->>Model: bootIfNotBooted()
    Model->>Trait: bootXxx() (una vez por clase)
    Model->>Model: initializeTraits()
    Model->>Trait: initializeXxx() (por instancia)
    Model-->>App: instancia del modelo
```

***

## Diferencia entre boot e initialize

|                           | `bootXxx()`                                                        | `initializeXxx()`                                             |
| ------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------- |
| **Momento de invocación** | La primera vez que arranca la clase del modelo (una vez por clase) | Cada vez que se crea una instancia                            |
| **Tipo de método**        | `static`                                                           | Método de instancia                                           |
| **Uso típico**            | Registrar listeners de eventos, añadir global scopes, etc.         | Establecer valores por defecto, inicializar propiedades, etc. |

***

## Convenciones de nomenclatura

### bootXxx()

Si defines un método **`static`** con el prefijo `boot` seguido del nombre del trait, se invocará una sola vez al arrancar el modelo.

```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();
            }
        });
    }
}
```

Uso desde el modelo:

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

class Article extends Model
{
    use HasSlug;
    // bootHasSlug() se invoca automáticamente
}
```

### initializeXxx()

Si defines un método de instancia con el prefijo `initialize` seguido del nombre del trait, se invocará cada vez que se haga `new` sobre el modelo.

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

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

También sirve para añadir dinámicamente entradas a `$casts`:

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

***

## Especificación explícita mediante PHP Attributes

A partir de Laravel 11 puedes indicar los métodos explícitamente con PHP Attributes, sin depender de la convención de nomenclatura.

| Attribute       | Objetivo                                                 |
| --------------- | -------------------------------------------------------- |
| `#[Boot]`       | Método que se ejecuta una única vez al arrancar la clase |
| `#[Initialize]` | Método que se ejecuta cada vez que se crea una instancia |

```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>
  Al usar PHP Attributes, el nombre del método deja de estar atado al nombre del trait, así que puedes elegir nombres realmente significativos. Además, puedes definir varios métodos `#[Boot]` o `#[Initialize]` dentro del mismo trait.
</Info>

***

## Ejemplos prácticos: uso en el desarrollo de paquetes

### Añadir un global scope automáticamente

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

### Añadir casts por defecto

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

### Procesamiento automático mediante eventos de Eloquent

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

***

## Ejemplos dentro del framework Laravel

El propio framework Laravel adopta este patrón en muchos de sus traits.

| Trait         | Método              | Qué hace                                               |
| ------------- | ------------------- | ------------------------------------------------------ |
| `SoftDeletes` | `bootSoftDeletes()` | Añade el global scope para soft delete                 |
| `HasFactory`  | `bootHasFactory()`  | Nada relevante (mecanismo fuera del alcance)           |
| `HasUlids`    | `bootHasUlids()`    | Asigna automáticamente un ULID en el evento `creating` |
| `HasUuids`    | `bootHasUuids()`    | Asigna automáticamente un UUID en el evento `creating` |

Leer estas implementaciones como referencia ayuda a entender el patrón con más profundidad.

***

## Consideraciones

### Limpiar la caché de modelos

Como `boot*` solo se ejecuta una vez por clase, hay que tener cuidado en el entorno de pruebas. Puedes resetear la caché de arranque con `Model::clearBootedModels()`.

```php theme={null}
// Resetea el estado boot de los modelos tras cada test
protected function tearDown(): void
{
    Model::clearBootedModels();
    parent::tearDown();
}
```

### Orden de ejecución

Cuando un mismo modelo hace `use` de varios traits, los métodos `bootXxx()` / `initializeXxx()` se invocan en el orden de resolución de traits de PHP (el orden de declaración). Si existen dependencias entre ellos, ten en cuenta el orden en el `use`.

***

## Resumen

| Patrón                      | Cómo se define                      | Cuándo aplicarlo                              |
| --------------------------- | ----------------------------------- | --------------------------------------------- |
| Arranque de la clase        | `static bootXxx()` o `#[Boot]`      | Registro de eventos, adición de global scopes |
| Inicialización de instancia | `initializeXxx()` o `#[Initialize]` | Valores por defecto, adición de casts         |

Implementar `bootXxx()` / `initializeXxx()` en un trait permite inyectar funcionalidad en un modelo con una sola línea `use`. Es el patrón habitual para añadir funciones a modelos desde un paquete.


## Related topics

- [Trait Conditionable](/es/advanced/conditionable.md)
- [Trait Dumpable](/es/advanced/dumpable.md)
- [Trait ForwardsCalls](/es/advanced/forwards-calls.md)
- [Factorías de Eloquent](/es/eloquent-factories.md)
- [Scopes de Eloquent](/es/advanced/eloquent-scopes.md)
