Cleaner Laravel Code with tap() and Higher-Order Messages
Some of Laravel's best features are the small ones you can easily go years without noticing. The tap() helper and higher-order messages on collections both exist to remove a specific, repetitive shape of code. Once you see them, you start spotting places to use them everywhere.
The tap() Helper
tap() takes a value, passes it to a closure so you can do something with it, and then returns the original value regardless of what the closure returns. That last part is the whole point.
Consider the common pattern of creating something, doing a side effect, and returning it:
$user = User::create($data);
$user->sendWelcomeEmail();
return $user;
With tap() that collapses into one expression:
return tap(User::create($data), function ($user) {
$user->sendWelcomeEmail();
});
tap() Without a Closure
Call tap() with just a value and it returns a proxy that forwards method calls to the object, then returns the object instead of the method's result. This is great for fluent-style updates on models, whose setters and update() normally return a boolean:
return tap($invoice)->update(['status' => 'paid']);
That returns the $invoice, not the boolean from update(), so you can keep chaining or return it directly.
Higher-Order Collection Messages
Collections support "higher-order messages," a shortcut syntax for methods that take a closure calling a single method on each item. Instead of writing the closure, you access the method as a property.
The verbose version:
$names = $users->map(function ($user) {
return $user->name;
});
$total = $orders->sum(function ($order) {
return $order->total;
});
The higher-order version:
$names = $users->map->name;
$total = $orders->sum->total;
These work on many collection methods, including each, map, filter, reject, sum, every, partition, and more.
// Send a notification to every subscriber
$subscribers->each->notify(new WeeklyDigest);
// Keep only the active accounts
$active = $accounts->filter->isActive();
When Not to Use Them
Higher-order messages only work when you are calling a single method or accessing a single property with no arguments. The moment you need a condition, a transformation, or arguments, go back to a normal closure. Readability is the goal, and forcing a clever one-liner where a plain closure is clearer defeats the purpose.
Both features are pure syntax sugar, but that is not a criticism. Code is read far more often than it is written, and removing the ceremony around common patterns makes intent easier to see at a glance.
Further Reading
- Laravel documentation, "Helpers", the
tapfunction (laravel.com/docs/13.x/helpers#method-tap) - Laravel documentation, "Collections", Higher Order Messages (laravel.com/docs/13.x/collections#higher-order-messages)