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

# Observers de Eloquent y eventos del modelo

> Explica cómo funcionan los eventos que dispara un modelo Eloquent y cómo centralizarlos mediante clases Observer. Incluye las novedades de Laravel 13 como el atributo `#[ObservedBy]`.

## Qué son los eventos del modelo

Los modelos Eloquent disparan eventos automáticamente en cada etapa de su ciclo de vida. Al engancharte a ellos puedes intercalar tu propia lógica antes o después de guardar, borrar, etc.

Los eventos que dispara Eloquent son los siguientes.

| Evento          | Momento en el que se dispara                                        |
| --------------- | ------------------------------------------------------------------- |
| `retrieved`     | Al recuperar un modelo de la BD.                                    |
| `creating`      | Justo antes de guardar un modelo nuevo.                             |
| `created`       | Justo después de guardar un modelo nuevo.                           |
| `updating`      | Justo antes de actualizar un modelo existente.                      |
| `updated`       | Justo después de actualizar un modelo existente.                    |
| `saving`        | Justo antes de guardar (tanto en creación como en actualización).   |
| `saved`         | Justo después de guardar (tanto en creación como en actualización). |
| `deleting`      | Justo antes de borrar el modelo.                                    |
| `deleted`       | Justo después de borrar el modelo.                                  |
| `trashed`       | Justo después de un soft delete.                                    |
| `forceDeleting` | Justo antes de un borrado físico.                                   |
| `forceDeleted`  | Justo después de un borrado físico.                                 |
| `restoring`     | Justo antes de restaurar un soft delete.                            |
| `restored`      | Justo después de restaurar un soft delete.                          |
| `replicating`   | Al llamar a `replicate()`.                                          |

Los eventos terminados en `-ing` se disparan **antes** de persistir los cambios; los terminados en `-ed`, **después**.

<Warning>
  Las actualizaciones y borrados masivos (como `User::where(...)->update(...)`) no disparan los eventos `saving`, `saved`, `updating`, `updated`, `deleting` ni `deleted`, porque en realidad no se recuperan modelos.
</Warning>

## Listeners de eventos mediante closures

Si quieres tratar los eventos de forma sencilla, puedes registrar closures dentro del método `booted` del modelo.

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

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected static function booted(): void
    {
        static::created(function (User $user) {
            // Se ejecuta después de crear un usuario
        });

        static::deleting(function (User $user) {
            // Se ejecuta antes de eliminar un usuario
        });
    }
}
```

Si quieres ejecutar la lógica de forma asíncrona en una cola, utiliza el helper `queueable`.

```php theme={null}
use function Illuminate\Events\queueable;

static::created(queueable(function (User $user) {
    // Se ejecuta de forma asíncrona en la cola
}));
```

## Propiedad `$dispatchesEvents`

Si quieres integrarte con el sistema de eventos de Laravel, la propiedad `$dispatchesEvents` te permite mapear eventos del modelo a tus propias clases de evento.

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

namespace App\Models;

use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    /**
     * Mapping entre eventos del modelo y clases de evento
     *
     * @var array<string, string>
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}
```

Las clases de evento mapeadas reciben la instancia del modelo en el constructor.

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

namespace App\Events;

use App\Models\User;

class UserSaved
{
    public function __construct(
        public readonly User $user,
    ) {}
}
```

## Crear una clase Observer

Si vas a manejar varios eventos de un mismo modelo, es más limpio reunirlos en una clase Observer que ir apilando closures.

<Steps>
  <Step title="Generar la clase con un comando Artisan">
    Genera el esqueleto con `make:observer`. Con la opción `--model` indica el modelo y se añaden automáticamente los métodos correspondientes.

    ```bash theme={null}
    php artisan make:observer UserObserver --model=User
    ```

    Se crea `app/Observers/UserObserver.php`.
  </Step>

  <Step title="Implementar los métodos de cada evento">
    Los nombres de los métodos se corresponden con los nombres de los eventos. El argumento recibe la instancia del modelo.

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

    namespace App\Observers;

    use App\Models\User;

    class UserObserver
    {
        public function created(User $user): void
        {
            // Lógica tras crear el usuario
        }

        public function updated(User $user): void
        {
            // Lógica tras actualizar el usuario
        }

        public function deleted(User $user): void
        {
            // Lógica tras eliminar el usuario
        }

        public function restored(User $user): void
        {
            // Lógica tras restaurar un soft delete
        }

        public function forceDeleted(User $user): void
        {
            // Lógica tras el borrado físico
        }
    }
    ```
  </Step>

  <Step title="Registrar el Observer en el modelo">
    Hay dos formas de registrarlo. En Laravel 13, la recomendación es usar el atributo `#[ObservedBy]`.

    **Opción 1: atributo `#[ObservedBy]` (recomendado)**

    Basta con añadir el atributo a la clase del modelo. No necesitas tocar `AppServiceProvider`.

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

    namespace App\Models;

    use App\Observers\UserObserver;
    use Illuminate\Database\Eloquent\Attributes\ObservedBy;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    #[ObservedBy(UserObserver::class)]
    class User extends Authenticatable
    {
        //
    }
    ```

    Para registrar varios observers, repite el atributo o pásalos como array.

    ```php theme={null}
    #[ObservedBy(UserObserver::class)]
    #[ObservedBy(AuditObserver::class)]
    class User extends Authenticatable
    {
        //
    }
    ```

    **Opción 2: registrarlo en `AppServiceProvider`**

    Llama a `observe` en el método `boot` de `AppServiceProvider`.

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

    namespace App\Providers;

    use App\Models\User;
    use App\Observers\UserObserver;
    use Illuminate\Support\ServiceProvider;

    class AppServiceProvider extends ServiceProvider
    {
        public function boot(): void
        {
            User::observe(UserObserver::class);
        }
    }
    ```
  </Step>
</Steps>

<Info>
  El atributo `#[ObservedBy]` está en el namespace `Illuminate\Database\Eloquent\Attributes`. Es sintaxis nativa de PHP 8.0 y Laravel 13 la utiliza de forma activa.
</Info>

## Observers dentro de transacciones de base de datos

Si un modelo se crea o actualiza dentro de una transacción, a veces querrás que el Observer se ejecute después del commit. Implementando la interfaz `ShouldHandleEventsAfterCommit` obtendrás ese comportamiento.

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

namespace App\Observers;

use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;

class UserObserver implements ShouldHandleEventsAfterCommit
{
    public function created(User $user): void
    {
        // Se ejecuta tras el commit de la transacción
    }
}
```

Fuera de transacciones, se ejecuta inmediatamente como de costumbre.

## Desactivar los eventos temporalmente

### `withoutEvents` para silenciar eventos solo en un bloque

Dentro del closure que pasas a `User::withoutEvents()`, no se dispara ningún evento del modelo.

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

$user = User::withoutEvents(function () {
    User::findOrFail(1)->delete();

    return User::find(2);
});
```

### `saveQuietly` para no disparar eventos al guardar

Si quieres guardar sin disparar eventos, utiliza `saveQuietly`.

```php theme={null}
$user = User::findOrFail(1);

$user->name = 'Victoria Faith';

$user->saveQuietly();
```

Existen métodos equivalentes para borrado, restauración y replicación.

```php theme={null}
$user->deleteQuietly();
$user->restoreQuietly();
```

## Casos de uso prácticos

### Limpieza automática de caché

Cuando un modelo se actualiza o se borra, limpia automáticamente la caché relacionada.

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

namespace App\Observers;

use App\Models\Post;
use Illuminate\Support\Facades\Cache;

class PostObserver
{
    public function saved(Post $post): void
    {
        Cache::forget("post:{$post->id}");
        Cache::forget('posts:latest');
    }

    public function deleted(Post $post): void
    {
        Cache::forget("post:{$post->id}");
        Cache::forget('posts:latest');
    }
}
```

### Registro de audit log

Registra automáticamente el histórico de cambios de un modelo. Con `getDirty()` obtienes los valores modificados.

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

namespace App\Observers;

use App\Models\AuditLog;
use App\Models\User;

class UserObserver
{
    public function updating(User $user): void
    {
        AuditLog::create([
            'model_type' => User::class,
            'model_id'   => $user->id,
            'changes'    => $user->getDirty(),
            'user_id'    => auth()->id(),
        ]);
    }

    public function deleted(User $user): void
    {
        AuditLog::create([
            'model_type' => User::class,
            'model_id'   => $user->id,
            'changes'    => ['deleted' => true],
            'user_id'    => auth()->id(),
        ]);
    }
}
```

<Tip>
  El evento `updating` se dispara **antes** de persistir en la BD, así que `getDirty()` te devuelve los valores que van a cambiar. Si lo llamas después de `updated`, `getDirty()` estará vacío.
</Tip>

### Actualización automática de modelos relacionados

Ejemplo para actualizar automáticamente el stock cuando se completa un pedido.

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

namespace App\Observers;

use App\Models\Order;

class OrderObserver
{
    public function created(Order $order): void
    {
        foreach ($order->items as $item) {
            $item->product->decrement('stock', $item->quantity);
        }
    }

    public function deleted(Order $order): void
    {
        foreach ($order->items as $item) {
            $item->product->increment('stock', $item->quantity);
        }
    }
}
```

## Próximos pasos

<Card title="Avanzado: Atributos de PHP" icon="tag" href="/es/advanced/php-attributes">
  Aprende los atributos de PHP de Laravel 13, incluido `#[ObservedBy]`, en un solo lugar.
</Card>


## Related topics

- [Introducción a Eloquent](/es/eloquent.md)
- [Seeding de base de datos](/es/seeding.md)
- [Casts personalizados de Eloquent](/es/advanced/eloquent-casts.md)
- [Eventos y listeners](/es/events.md)
- [Eloquent Bootable Traits](/es/advanced/eloquent-bootable-traits.md)
