> ## 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-observers en modelevents

> Uitleg over het eventmechanisme van Eloquent-modellen en hoe je events centraal beheert met observerklassen. Inclusief de nieuwste functies van Laravel 13, zoals het `#[ObservedBy]`-attribute.

## Wat zijn modelevents?

Een Eloquent-model vuurt automatisch events af op verschillende momenten in zijn levenscyclus. Door op deze events in te haken kun je verwerking toevoegen vóór en na het opslaan, verwijderen, enzovoort.

De events die Eloquent afvuurt:

| Event           | Moment                                                  |
| --------------- | ------------------------------------------------------- |
| `retrieved`     | Wanneer het model uit de DB wordt opgehaald             |
| `creating`      | Vlak vóór het opslaan van een nieuw model               |
| `created`       | Vlak ná het opslaan van een nieuw model                 |
| `updating`      | Vlak vóór het bijwerken van een bestaand model          |
| `updated`       | Vlak ná het bijwerken van een bestaand model            |
| `saving`        | Vlak vóór het opslaan, bij zowel aanmaken als bijwerken |
| `saved`         | Vlak ná het opslaan, bij zowel aanmaken als bijwerken   |
| `deleting`      | Vlak vóór het verwijderen van het model                 |
| `deleted`       | Vlak ná het verwijderen van het model                   |
| `trashed`       | Vlak ná een soft delete                                 |
| `forceDeleting` | Vlak vóór een definitieve verwijdering                  |
| `forceDeleted`  | Vlak ná een definitieve verwijdering                    |
| `restoring`     | Vlak vóór het herstellen van een soft delete            |
| `restored`      | Vlak ná het herstellen van een soft delete              |
| `replicating`   | Wanneer `replicate()` wordt aangeroepen                 |

Events die eindigen op `-ing` gaan af **vóórdat** de wijziging in de DB wordt vastgelegd; events die eindigen op `-ed` gaan **daarna** af.

<Warning>
  Bij mass updates en mass deletes (zoals `User::where(...)->update(...)`) worden de events `saving`, `saved`, `updating`, `updated`, `deleting` en `deleted` niet afgevuurd, omdat de modellen niet daadwerkelijk worden opgehaald.
</Warning>

## Eventlisteners met closures

Wil je events simpel afhandelen, dan kun je closures registreren in de `booted`-methode van het model.

```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) {
            // Verwerking die wordt uitgevoerd na het aanmaken van de gebruiker
        });

        static::deleting(function (User $user) {
            // Verwerking die wordt uitgevoerd vóór het verwijderen van de gebruiker
        });
    }
}
```

Wil je de verwerking asynchroon via een queue uitvoeren, gebruik dan de `queueable`-helper.

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

static::created(queueable(function (User $user) {
    // Wordt asynchroon via de queue uitgevoerd
}));
```

## De property `$dispatchesEvents`

Wil je integreren met het eventsysteem van Laravel, dan map je modelevents met de property `$dispatchesEvents` naar eigen eventklassen.

```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 van modelevents naar eventklassen
     *
     * @var array<string, string>
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}
```

De gemapte eventklasse ontvangt de modelinstantie in zijn constructor.

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

namespace App\Events;

use App\Models\User;

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

## Een observerklasse maken

Verwerk je meerdere events voor één model, dan is een observerklasse overzichtelijker dan een reeks closures.

<Steps>
  <Step title="De klasse genereren met een Artisan-commando">
    Genereer een sjabloon met het commando `make:observer`. Geef je met de optie `--model` het model op, dan worden de bijbehorende methodes automatisch toegevoegd.

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

    Er wordt een `app/Observers/UserObserver.php` gegenereerd.
  </Step>

  <Step title="De methodes voor de events implementeren">
    De methodenaam komt overeen met de eventnaam. Als argument wordt de modelinstantie doorgegeven.

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

    namespace App\Observers;

    use App\Models\User;

    class UserObserver
    {
        public function created(User $user): void
        {
            // Verwerking na het aanmaken van de gebruiker
        }

        public function updated(User $user): void
        {
            // Verwerking na het bijwerken van de gebruiker
        }

        public function deleted(User $user): void
        {
            // Verwerking na het verwijderen van de gebruiker
        }

        public function restored(User $user): void
        {
            // Verwerking na het herstellen van een soft delete
        }

        public function forceDeleted(User $user): void
        {
            // Verwerking na definitieve verwijdering
        }
    }
    ```
  </Step>

  <Step title="De observer registreren bij het model">
    Er zijn twee manieren om te registreren. In Laravel 13 heeft het `#[ObservedBy]`-attribute de voorkeur.

    **Manier 1: het `#[ObservedBy]`-attribute (aanbevolen)**

    Alleen het attribute op de modelklasse zetten is genoeg; je hoeft de `AppServiceProvider` niet aan te passen.

    ```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
    {
        //
    }
    ```

    Wil je meerdere observers registreren, herhaal dan het attribute of geef een array door.

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

    **Manier 2: registreren in de `AppServiceProvider`**

    Roep `observe` aan in de `boot`-methode van 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>
  Het `#[ObservedBy]`-attribute staat in de namespace `Illuminate\Database\Eloquent\Attributes`. Het is native syntaxis van PHP 8.0 en later en wordt in Laravel 13 actief toegepast.
</Info>

## Observers binnen databasetransacties

Wordt een model binnen een transactie aangemaakt of bijgewerkt, dan wil je de observer soms pas na de commit van de transactie laten draaien. Implementeer daarvoor de interface `ShouldHandleEventsAfterCommit`.

```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
    {
        // Wordt uitgevoerd nadat de transactie is gecommit
    }
}
```

Gebeurt de uitvoering buiten een transactie, dan wordt de observer zoals gebruikelijk direct uitgevoerd.

## Events tijdelijk uitschakelen

### Alleen voor een specifieke bewerking events stoppen met `withoutEvents`

Binnen de closure die je doorgeeft aan `User::withoutEvents()` worden geen enkele modelevents afgevuurd.

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

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

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

### Events bij het opslaan stoppen met `saveQuietly`

Wil je een model opslaan zonder events af te vuren, gebruik dan `saveQuietly`.

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

$user->name = 'Victoria Faith';

$user->saveQuietly();
```

Vergelijkbare methodes bestaan ook voor verwijderen, herstellen en repliceren.

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

## Praktische use cases

### Automatisch de cache legen

Automatisch de gerelateerde cache legen wanneer een model wordt bijgewerkt of verwijderd.

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

### Auditlogs bijhouden

De wijzigingsgeschiedenis van een model automatisch vastleggen. Met `getDirty()` haal je de gewijzigde waarden op.

```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>
  Het `updating`-event gaat af **vóór** het opslaan in de DB, dus met `getDirty()` haal je de nog door te voeren waarden op. Roep je het aan na het `updated`-event, dan is `getDirty()` leeg.
</Tip>

### Gerelateerde modellen automatisch bijwerken

Een voorbeeld dat automatisch de voorraad bijwerkt wanneer een bestelling is geplaatst.

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

## Volgende stap

<Card title="Geavanceerd: PHP-attributes" icon="tag" href="/nl/advanced/php-attributes">
  Leer de PHP-attributes van Laravel 13 kennen, inclusief `#[ObservedBy]`.
</Card>


## Related topics

- [Custom casts van Eloquent](/nl/advanced/eloquent-casts.md)
- [Querybuilder](/nl/query-builder.md)
- [Laravel Telescope](/nl/telescope.md)
- [Laravel Scout](/nl/scout.md)
- [Uitgestelde service providers](/nl/advanced/deferred-provider.md)
