> ## 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-Observer und Modell-Events

> Wie Eloquent-Modelle Events auslösen und wie Sie diese über Observer-Klassen zentral verwalten. Inklusive der neuen Features von Laravel 13 wie dem Attribut `#[ObservedBy]`.

## Was sind Modell-Events?

Eloquent-Modelle lösen an bestimmten Punkten ihres Lebenszyklus automatisch Events aus. Indem Sie sich an diese Events hängen, können Sie vor oder nach dem Speichern, Löschen usw. eigene Logik einschleusen.

Die von Eloquent ausgelösten Events sind:

| Event           | Zeitpunkt                                                    |
| --------------- | ------------------------------------------------------------ |
| `retrieved`     | Nachdem das Modell aus der Datenbank geladen wurde           |
| `creating`      | Unmittelbar vor dem Speichern eines neuen Modells            |
| `created`       | Unmittelbar nach dem Speichern eines neuen Modells           |
| `updating`      | Unmittelbar vor dem Aktualisieren eines bestehenden Modells  |
| `updated`       | Unmittelbar nach dem Aktualisieren eines bestehenden Modells |
| `saving`        | Unmittelbar vor dem Speichern (Neuanlage oder Update)        |
| `saved`         | Unmittelbar nach dem Speichern (Neuanlage oder Update)       |
| `deleting`      | Unmittelbar vor dem Löschen eines Modells                    |
| `deleted`       | Unmittelbar nach dem Löschen eines Modells                   |
| `trashed`       | Unmittelbar nach einem Soft Delete                           |
| `forceDeleting` | Unmittelbar vor einem harten Löschen                         |
| `forceDeleted`  | Unmittelbar nach einem harten Löschen                        |
| `restoring`     | Unmittelbar vor dem Wiederherstellen eines Soft-Deletes      |
| `restored`      | Unmittelbar nach dem Wiederherstellen eines Soft-Deletes     |
| `replicating`   | Beim Aufruf von `replicate()`                                |

Events, die auf `-ing` enden, feuern **vor** der Persistierung in die DB, Events auf `-ed` **danach**.

<Warning>
  Bei Massen-Updates oder Massen-Deletes (z. B. `User::where(...)->update(...)`) werden die Events `saving`, `saved`, `updating`, `updated`, `deleting`, `deleted` nicht ausgelöst, weil die Modelle nicht instanziiert werden.
</Warning>

## Event-Listener über Closures

Für einfache Event-Behandlung können Sie Closures in der `booted`-Methode des Modells registrieren.

```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) {
            // Läuft nach dem Anlegen eines Nutzers
        });

        static::deleting(function (User $user) {
            // Läuft vor dem Löschen eines Nutzers
        });
    }
}
```

Um die Verarbeitung asynchron in einer Queue auszuführen, verwenden Sie den Helper `queueable`.

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

static::created(queueable(function (User $user) {
    // Wird asynchron in der Queue ausgeführt
}));
```

## Property `$dispatchesEvents`

Wenn Sie das Laravel-Event-System nutzen möchten, ordnen Sie über die Property `$dispatchesEvents` Modell-Events eigenen Event-Klassen zu.

```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
{
    /**
     * Zuordnung von Modell-Events zu Event-Klassen
     *
     * @var array<string, string>
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}
```

Die zugeordneten Event-Klassen erhalten die Modell-Instanz per Konstruktor.

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

namespace App\Events;

use App\Models\User;

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

## Eine Observer-Klasse anlegen

Wenn Sie mehrere Events desselben Modells bündeln möchten, ist eine Observer-Klasse übersichtlicher als eine Reihe von Closures.

<Steps>
  <Step title="Klasse per Artisan-Command erzeugen">
    Erzeugen Sie ein Gerüst mit `make:observer`. Mit der Option `--model` erhalten Sie passende Methoden.

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

    Die Datei `app/Observers/UserObserver.php` wird angelegt.
  </Step>

  <Step title="Methoden je Event implementieren">
    Der Methodenname entspricht dem Event-Namen. Als Argument erhalten Sie die Modell-Instanz.

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

    namespace App\Observers;

    use App\Models\User;

    class UserObserver
    {
        public function created(User $user): void
        {
            // Verarbeitung nach dem Anlegen des Nutzers
        }

        public function updated(User $user): void
        {
            // Verarbeitung nach dem Aktualisieren
        }

        public function deleted(User $user): void
        {
            // Verarbeitung nach dem Löschen
        }

        public function restored(User $user): void
        {
            // Verarbeitung nach dem Wiederherstellen eines Soft-Deletes
        }

        public function forceDeleted(User $user): void
        {
            // Verarbeitung nach hartem Löschen
        }
    }
    ```
  </Step>

  <Step title="Observer am Modell registrieren">
    Es gibt zwei Registrierungsvarianten. In Laravel 13 wird das Attribut `#[ObservedBy]` empfohlen.

    **Variante 1: Attribut `#[ObservedBy]` (empfohlen)**

    Sie versehen die Modellklasse mit dem Attribut — die Registrierung ist damit abgeschlossen. Änderungen am `AppServiceProvider` sind nicht nötig.

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

    Zum Registrieren mehrerer Observer wiederholen Sie das Attribut oder übergeben ein Array.

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

    **Variante 2: Registrierung im `AppServiceProvider`**

    Rufen Sie in der `boot`-Methode `observe` auf.

    ```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>
  Das Attribut `#[ObservedBy]` liegt im Namensraum `Illuminate\Database\Eloquent\Attributes` und ist native PHP-Syntax ab 8.0, die Laravel 13 aktiv einsetzt.
</Info>

## Observer innerhalb von Datenbanktransaktionen

Werden Modelle innerhalb einer Transaktion erstellt oder aktualisiert, wollen Sie den Observer manchmal erst nach dem Commit ausführen. Implementieren Sie dazu das 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
    {
        // Läuft nach dem Commit der Transaktion
    }
}
```

Wird außerhalb einer Transaktion gearbeitet, läuft der Observer wie gewohnt sofort.

## Events temporär deaktivieren

### `withoutEvents` — Events während eines Codeabschnitts unterdrücken

Innerhalb der an `User::withoutEvents()` übergebenen Closure werden keinerlei Modell-Events ausgelöst.

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

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

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

### `saveQuietly` — Events beim Speichern unterdrücken

Wenn Sie ein Modell speichern wollen, ohne Events auszulösen, nutzen Sie `saveQuietly`.

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

$user->name = 'Victoria Faith';

$user->saveQuietly();
```

Analog gibt es Methoden für Löschen, Wiederherstellen und Replizieren.

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

## Praktische Anwendungsfälle

### Cache automatisch invalidieren

Wenn ein Modell aktualisiert oder gelöscht wird, den zugehörigen Cache automatisch leeren.

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

### Audit-Logs schreiben

Änderungshistorien automatisch protokollieren. Mit `getDirty()` erhalten Sie die Werte vor und nach der Änderung.

```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>
  Das Event `updating` feuert **vor** der Persistierung in die DB — hier liefert `getDirty()` die geplanten neuen Werte. Nach dem Event `updated` ist `getDirty()` bereits leer.
</Tip>

### Verknüpfte Modelle automatisch aktualisieren

Beispiel: Nach Abschluss einer Bestellung den Lagerbestand automatisch anpassen.

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

## Nächste Schritte

<Card title="Fortgeschritten: PHP-Attribute" icon="tag" href="/de/advanced/php-attributes">
  Lernen Sie die PHP-Attribute von Laravel 13 inklusive `#[ObservedBy]` im Überblick.
</Card>


## Related topics

- [Eigene Casts in Eloquent](/de/advanced/eloquent-casts.md)
- [Query Builder](/de/query-builder.md)
- [Laravel Scout](/de/scout.md)
- [Einführung in Eloquent](/de/eloquent.md)
- [Events und Listener](/de/events.md)
