Zum Hauptinhalt springen

Was ist tap()?

tap() ist ein Helper, der „einen Wert benutzt und ihn dennoch unverändert zurückgibt”. Er kommt zum Einsatz, wenn Sie einen Seiteneffekt einschleifen möchten. Die Implementierung in Illuminate\Support\helpers.php ist schlicht:
function tap($value, $callback = null)
{
    if (is_null($callback)) {
        return new HigherOrderTapProxy($value);
    }

    $callback($value);

    return $value;
}
tap() ignoriert den Rückgabewert des Callbacks. Zurückgegeben wird stets der ursprüngliche Wert.

Grundlegende Verwendung

Der grundlegendste Einsatz besteht darin, den Wert zu übernehmen, etwas damit zu tun und ihn unverändert zurückzugeben.
use App\Models\User;

$user = tap(User::query()->latest()->firstOrFail(), function (User $model) {
    $model->update(['last_seen_at' => now()]);
});

// Zurückgegeben wird $user – unabhängig vom Rückgabewert von update()
Ohne Callback erhalten Sie einen HigherOrderTapProxy — so lässt sich der Methodenaufruf direkt anfügen.
$updatedUser = tap($user)->update([
    'name' => $name,
    'email' => $email,
]);

// Zurückgegeben wird nicht der Rückgabewert von update(), sondern die ursprüngliche User-Instanz

Typische Einsatzstellen

Debug-Ausgabe einfügen

$result = tap($query->get(), function ($users) {
    logger()->debug('Fetched users', ['count' => $users->count()]);
});

Logging oder Event-Versand innerhalb der Chain einfügen

$order = tap(Order::create($payload), function (Order $order) {
    event(new OrderCreated($order));
    logger()->info('Order created', ['id' => $order->id]);
});

Seiteneffekte ausführen, ohne den Rückgabewert zu verändern

$response = tap($service->handle($request), function ($response) {
    \Illuminate\Support\Facades\Cache::increment('service_handle_success_total');
});
Wollen Sie den Rückgabewert transformieren, nutzen Sie with() oder eine gewöhnliche Variablenzuweisung. Beschränken Sie tap() auf Seiteneffekte — das steigert die Lesbarkeit.

Was ist der Tappable-Trait?

Wenn Sie Illuminate\Support\Traits\Tappable per use einbinden, erhält Ihre Klasse eine tap()-Methode.
trait Tappable
{
    public function tap($callback = null)
    {
        return tap($this, $callback);
    }
}
Kurz gesagt bietet der Trait lediglich eine Instanzmethoden-Variante von tap().
use Illuminate\Support\Traits\Tappable;

class ReportBuilder
{
    use Tappable;
}

$builder = new ReportBuilder();

$builder = $builder->tap(function (ReportBuilder $instance) {
    logger()->debug('builder initialized');
});

Einsatz in der Paketentwicklung

Tappable ist praktisch, wenn Sie in einer Fluent API einen Seiteneffekt einbauen möchten. In Kombination mit Macroable und Conditionable bauen Sie einen Laravel-typischen, erweiterbaren Builder.
1

Eine Fluent-Klasse anlegen

use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Support\Traits\Tappable;

class QueryPresetBuilder
{
    use Macroable;
    use Conditionable;
    use Tappable;

    protected array $filters = [];

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

        return $this;
    }

    public function toArray(): array
    {
        return $this->filters;
    }
}
2

Macroable, Conditionable und Tappable kombiniert nutzen

QueryPresetBuilder::macro('forActiveUsers', function () {
    /** @var QueryPresetBuilder $this */
    return $this->where('active', true);
});

$filters = (new QueryPresetBuilder)
    ->forActiveUsers()
    ->when($request->filled('role'), fn ($builder) => $builder->where('role', $request->role))
    ->tap(fn ($builder) => logger()->debug('current filters', $builder->toArray()))
    ->toArray();
Verwandte Seiten:

Verwendung im Laravel-Kern

Auch im Laravel-Kern selbst kommen tap() und Tappable zum Einsatz.
  • Illuminate\Routing\Router verwendet Macroable und Tappable gemeinsam.
  • In Router::respondWithRoute() wird ohne Callback tap($route)->bind(...) genutzt und die ursprüngliche $route auch nach bind() zurückgegeben.
  • In Router::prepareResponse() wird nach der Response-Konvertierung mit tap(..., fn (...) => event(...)) ein Event ausgelöst.
  • In Illuminate\Testing\Fluent\Concerns\Has unterstützt der Aufruf ->tap(...)->first(...)->etc() die Assertions.
// Aus Illuminate\Routing\Router
$route = tap($this->routes->getByName($name))->bind($this->currentRequest);

return tap(static::toResponse($request, $response), function ($response) use ($request) {
    $this->events->dispatch(new ResponsePrepared($request, $response));
});
Für sich genommen ist tap() ein kleiner Helper — in Kombination mit Macroable und Conditionable entstehen jedoch gut lesbare Method-Chains, wie man sie in Laravel häufig sieht.

Nächste Schritte

Macroable-Trait

Lernen Sie das Design kennen, mit dem Sie bestehende Klassen um eigene Methoden erweitern.

Conditionable-Trait

Lernen Sie Bedingungs-Chains mit when() / unless() kennen.
Zuletzt geändert am 13. Juli 2026