Zum Hauptinhalt springen

Was ist der Conditionable-Trait?

Der Trait Illuminate\Support\Traits\Conditionable erweitert ein Objekt um die Methoden when() und unless(). Sein Kennzeichen: Sie können Verzweigungen einbauen und dennoch die Method-Chain fortsetzen.
Der eigentliche Quellcode befindet sich unter src/Illuminate/Conditionable/Traits/Conditionable.php und wird über den Alias Illuminate\Support\Traits\Conditionable referenziert.
Viele Klassen von Laravel wie QueryBuilder, EloquentBuilder, Mail oder Notification setzen diesen Trait ein.

Grundlegende Verwendung

when() — bei wahrer Bedingung ausführen

use Illuminate\Support\Collection;

$results = collect([1, 2, 3, 4, 5])
    ->when(true, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n > 2);
    });
// [3, 4, 5]
Ist der Wert des ersten Arguments wahr, wird der Callback des zweiten Arguments ausgeführt. Ist er falsch, wird der Standard-Callback des dritten Arguments ausgeführt.
$results = collect([1, 2, 3, 4, 5])
    ->when(false, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n > 2);
    }, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n < 3);
    });
// [1, 2]

unless() — bei falscher Bedingung ausführen

unless() ist die Umkehrung von when(). Der Callback wird ausgeführt, wenn die Bedingung falsch ist.
$results = collect([1, 2, 3, 4, 5])
    ->unless(false, function (Collection $collection) {
        return $collection->take(3);
    });
// [1, 2, 3]

Warum die Method-Chain fortgesetzt wird

Gibt der Callback null zurück, wird $this (das Objekt, das den Trait verwendet) zurückgegeben. Gibt der Callback etwas anderes als null zurück, wird dieser Rückgabewert weitergereicht.
// Da $this zurückgegeben wird, kann die Chain weiterlaufen
$query = User::query()
    ->when($request->has('active'), function ($query) {
        $query->where('active', true); // gibt void / null zurück
    })
    ->when($request->filled('name'), function ($query) use ($request) {
        $query->where('name', 'like', "%{$request->name}%");
    })
    ->orderBy('created_at', 'desc');
Der Quellcode ist wie folgt implementiert:
if ($value) {
    return $callback($this, $value) ?? $this;
} elseif ($default) {
    return $default($this, $value) ?? $this;
}

return $this;
Gibt der Callback einen expliziten Wert zurück, wird dieser Wert an die Chain weitergegeben. Gibt er nichts (null) zurück, wird $this weitergereicht.

Aufruf ohne Argumente — HigherOrderWhenProxy

Ruft man when() ganz ohne Argumente auf, wird ein HigherOrderWhenProxy zurückgegeben. Damit können Sie die Bedingung nachträglich festlegen.
$query = User::query()
    ->when()->isActive()  // isActive() wird als Bedingung ausgewertet
    ->where('role', 'admin');
Mit einem einzigen Argument erhalten Sie einen Proxy, der diesen Wert als Bedingung speichert.
// Ein Argument: nur die Bedingung übergeben, Proxy erhalten
$proxy = collect([1, 2, 3])->when($request->has('filter'));
// Über $proxy->methodName() lässt sich methodName() nur bei wahrer Bedingung aufrufen

Eine Closure als Wert übergeben

Wird als erstes Argument eine Closure übergeben, wird deren Rückgabewert als Bedingung verwendet.
$results = User::query()
    ->when(
        fn ($query) => $request->filled('role'),
        fn ($query) => $query->where('role', $request->role)
    )
    ->get();
So können Sie die Auswertungslogik der Bedingung in einen Callback auslagern.

Typisches Muster im QueryBuilder

Der dynamische Aufbau einer Query mit when() ist der häufigste Anwendungsfall.
public function index(Request $request)
{
    $users = User::query()
        ->when($request->filled('search'), function ($query) use ($request) {
            $query->where('name', 'like', "%{$request->search}%")
                  ->orWhere('email', 'like', "%{$request->search}%");
        })
        ->when($request->filled('role'), fn ($q) => $q->where('role', $request->role))
        ->when($request->boolean('verified'), fn ($q) => $q->whereNotNull('email_verified_at'))
        ->when(
            $request->filled('sort'),
            fn ($q) => $q->orderBy($request->sort, $request->get('direction', 'asc')),
            fn ($q) => $q->latest()
        )
        ->paginate();

    return UserResource::collection($users);
}

Conditionable in eigenen Klassen einsetzen

Nach einem einfachen use des Traits stehen when() und unless() zur Verfügung.
namespace App\Services;

use Illuminate\Support\Traits\Conditionable;

class ReportBuilder
{
    use Conditionable;

    protected array $filters = [];
    protected bool $includeArchived = false;
    protected ?string $groupBy = null;

    public function withArchived(): static
    {
        $this->includeArchived = true;

        return $this;
    }

    public function groupBy(string $column): static
    {
        $this->groupBy = $column;

        return $this;
    }

    public function addFilter(string $column, mixed $value): static
    {
        $this->filters[$column] = $value;

        return $this;
    }

    public function build(): \Illuminate\Database\Eloquent\Builder
    {
        return Report::query()
            ->when($this->includeArchived, fn ($q) => $q->withTrashed())
            ->when($this->groupBy, fn ($q) => $q->groupBy($this->groupBy))
            ->when($this->filters, function ($q) {
                foreach ($this->filters as $column => $value) {
                    $q->where($column, $value);
                }
            });
    }
}
// Anwendungsbeispiel
$query = (new ReportBuilder)
    ->when($request->boolean('archived'), fn ($b) => $b->withArchived())
    ->when($request->filled('group'), fn ($b) => $b->groupBy($request->group))
    ->addFilter('status', 'published')
    ->build();

Einsatz bei Mail, Notification und Response

when() lässt sich auch beim Aufbau von Mails, Notifications und Responses nutzen.
use Illuminate\Mail\Mailable;
use Illuminate\Support\Traits\Conditionable;

class OrderConfirmation extends Mailable
{
    public function build(): static
    {
        return $this
            ->subject('Ihre Bestellung wurde angenommen')
            ->view('emails.order.confirmation')
            ->when($this->order->hasDiscount(), function (Mailable $mail) {
                $mail->attach(storage_path('discounts/coupon.pdf'));
            })
            ->when(app()->environment('production'), function (Mailable $mail) {
                $mail->bcc('[email protected]');
            });
    }
}

Abgrenzung zu tap()

tap() und when() ähneln sich, verfolgen aber unterschiedliche Ziele.
tap()when()
ZweckSeiteneffekte (Logging, Debugging)Verzweigung
Rückgabewertimmer $thisbedingt $this oder der Rückgabewert des Callbacks
Bedingungneinja
// tap: für Seiteneffekte. Der Rückgabewert ist immer $this
$user = User::find($id)
    ->tap(fn ($user) => Log::info("User {$user->id} loaded"));

// when: für Verzweigung
$user = User::query()
    ->when($isAdmin, fn ($q) => $q->where('role', 'admin'))
    ->first();
Wollen Sie nur Debugging oder Seiteneffekte innerhalb der Chain unterbringen, nutzen Sie tap(). Wollen Sie den Ablauf abhängig von einer Bedingung umschalten, verwenden Sie when() / unless().

Nächste Schritte

Higher-Order Messages in Collections

Lernen Sie Funktionsweise und praktischen Einsatz von Syntax wie $collection->map->method() kennen.
Zuletzt geändert am 13. Juli 2026