laravel-tap-side-effects-keep-original-value-tip.png
Шаблон кодуLaravel

Laravel tap(): Perform an Action and Keep the Original Value

Use tap() for a side effect without losing the original value, return a model after update(), use the callback form and Tappable trait, and understand when tap() is not a transformation tool.

Sometimes you need to perform an action on a value and then continue with that exact original value. Without tap(), this often becomes a temporary variable, a few mutation lines, and a separate return.

Laravel tap() makes the intent explicit: perform a side effect with this value, then give me the same value back.

Basic Case: Mutate a Model and Return It

До
php
$user->update([    'name' => $name,    'email' => $email,]); return $user;
Після
php
return tap($user)->update([    'name' => $name,    'email' => $email,]);

Without tap(), the result of calling update() would be the method's own return value. The higher-order form tap($user)->update(...) intentionally ignores that value and returns the original $user object.

The point is not shorter syntax at any cost. The helper is useful when you want to preserve the fluent flow and return the model after acting on it.

The Callback Form Shows the Core tap() Model

PHP - tap() with a callback
$user = tap(    $user,    function (User $user) use ($name): void {        $user->name = $name;        $user->save();    },);

The first argument is passed to the callback, and after the callback runs tap() returns the original value. Whatever the callback itself returns does not become the result of tap().

The Callback Return Value Is Ignored

PHP - The callback result does not replace the original value
$result = tap(    $user,    function (User $user): string {        $user->touch();         return 'ignored';    },); // $result is still the User instance.
tap() is not a value transformation tool. If you need the callback result, return it explicitly with normal code or use an API designed for transformation.

tap() Fits Small Side Effects

PHP - Persist the model and run a related side effect
return tap($order, function (Order $order): void {    $order->update([        'status' => OrderStatus::PAID,    ]);     Log::info('Order marked as paid', [        'order_id' => $order->id,    ]);});

This form is useful when the main result of the method is still the model itself, while one local side effect needs to happen before returning it: persisting changes, recording diagnostic information, or preparing the object.

tap() Is Also Useful Without Mutation

PHP - Inspect a value without breaking the flow
$payload = tap(    $payload,    fn (array $payload) => Log::debug(        'Webhook payload',        ['payload' => $payload],    ),);

The callback does not need to mutate the value. tap() is also useful for observing an intermediate value when the same result should continue through the rest of the flow.

Higher-Order tap() Removes a One-Method Callback

До
php
return tap(    $user,    function (User $user) use ($attributes): void {        $user->update($attributes);    },);
Після
php
return tap($user)    ->update($attributes);

When the callback only performs one method call, Laravel lets you omit the closure. tap($value) returns a proxy that calls the method on the original object and then returns that object again.

The Tappable Trait Adds tap() to Your Own Class

PHP - A custom class using Tappable
use Illuminate\Support\Traits\Tappable; final class Report{    use Tappable;} return $report->tap(    function (Report $report): void {        $report->prepare();    },);

If this style naturally belongs to your own object, Laravel provides the Tappable trait. Its tap() method passes the current instance to the closure and then returns the same instance.

Query Builder Has tap() Too, but It Is a Separate API

PHP - Query Builder tap() for reusable query logic
DB::table('users')    ->tap(new ActiveUsersFilter())    ->orderBy('name')    ->get();

Modern Query Builder also exposes tap() for applying reusable query logic while always returning the builder. The idea is related - perform an action and preserve the current flow - but this is a separate API from the global tap($value) helper.

tap() Does Not Make Work Transactional

tap() controls which value is returned after a callback or method call. It does not add a transaction, change Eloquent persistence rules, or make several side effects atomic.

If the callback performs several database writes that must succeed together, you still need an appropriate transaction boundary. tap() solves a flow problem, not a consistency problem.

Do Not Hide a Large Business Operation Inside tap()

До
php
return tap($order, function (Order $order): void {    $order->reserveStock();    $order->chargeCustomer();    $order->generateInvoice();    $order->notifyWarehouse();    $order->dispatchShipment();});
Після
php
$order = $checkoutService->complete($order); return $order;

A small helper should not hide a large workflow. If the callback grows into several meaningful business steps, a dedicated service or a clearly named method usually communicates the intent better.

When tap() Is a Good Fit

  • you need to act on an object and immediately return that same object;
  • you want to insert one small side effect into an otherwise clear flow;
  • you want to inspect or log an intermediate value without changing the result;
  • a method returns a technical result but the caller needs the original object;
  • a small callback makes the intent clearer instead of hiding complexity.

When Plain Code Is Clearer

  • when you need the value returned by the callback;
  • when the result should be transformed into another type or object;
  • when a complex business workflow is accumulating inside the callback;
  • when a variable and two explicit lines are easier to understand than an unfamiliar helper;
  • when you need a transaction or another execution guarantee that tap() does not provide.

The purpose of tap() is intentionally narrow: do something with this value, but do not change what flows outward. When that intent is not obvious, plain code is often the better choice.

#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #tap #helpers #tappable

Схожі поради

laravel-withwherehas-avoid-duplicate-conditions-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #where-has #with-where-has #eager-loading #relationships
laravel-firstorcreate-replace-first-create-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #model-persistence #first-or-create #first-or-new
laravel-updateorcreate-replace-first-if-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #update-or-create #model-persistence #upsert
laravel-whererelation-replace-wherehas-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #where-relation #where-has #relationship-queries