Home Posts Projects Contact
Home Posts Projects Contact

Laravel's Hidden Gem: The tap() Helper (And Why It'll Make You Wonder How You Ever Lived Without It)

Ever find yourself doing this awkward dance in Laravel?

$user = User::create([
    'name' => 'Kyle Anderson',
    'email' => '[email protected]',
]);

$user->assignRole('developer');
$user->notify(new WelcomeNotification());

return $user;

It works, but it feels... cluttered. You're creating an object, then immediately mutating it across multiple lines, then returning it. There's got to be a better way, right?

Enter: tap()

Laravel's tap() helper is like that friend who holds your coffee while you tie your shoes—it takes care of something temporarily so you can focus on what matters, then hands it right back.

Here's the same code using tap():

return tap(User::create([
    'name' => 'Kyle Anderson',
    'email' => '[email protected]',
]), function ($user) {
    $user->assignRole('developer');
    $user->notify(new WelcomeNotification());
});

Clean, elegant, and it returns the $user object automatically. Chef's kiss.

But Wait, There's More!

The really cool part? You can chain it for even cleaner code:

return tap(new User())->fill([
    'name' => 'Kyle Anderson',
    'email' => '[email protected]',
])->save();

Or use it for debugging without breaking your chain:

$users = User::query()
    ->where('active', true)
    ->tap(fn($query) => logger('Active users query:', ['sql' => $query->toSql()]))
    ->get();

The query keeps flowing, but you've sneakily logged it mid-stream. Debugging level: stealth mode activated.

My Favorite Use Case: Config Objects

Here's where tap() really shines—building configuration objects:

$options = tap(new PaymentOptions(), function ($opts) {
    $opts->setCurrency('USD');
    $opts->setAmount(4999);
    $opts->enableRecurring();
    $opts->setDescription('Premium Subscription');
});

$payment->process($options);

No more creating the object, setting properties on separate lines, then passing it around. It's all bundled up nice and tidy.

The "Why Though?" Moment

You might be thinking: "Kyle, this is just syntactic sugar. Does it really matter?"

Fair point! But here's the thing—tap() isn't about doing something you can't do otherwise. It's about making your intent crystal clear. When you see tap(), you immediately know: "We're creating/modifying this thing temporarily, doing some stuff with it, then moving on."

Plus, it keeps your code's "return value" obvious. No more hunting through 10 lines wondering what actually gets returned.

One Gotcha to Watch Out For

tap() returns the original object, not the result of the callback. So this won't work how you think:

$doubled = tap(5, fn($n) => $n * 2); // Returns 5, not 10 ❌

If you need the callback's return value, use with() instead:

$doubled = with(5, fn($n) => $n * 2); // Returns 10 ✅

The Bottom Line

tap() is one of those Laravel helpers that seems small but ends up in your code everywhere once you start using it. It's like discovering keyboard shortcuts—once you know them, going back feels painful.

Give it a shot in your next project. Your future self (and your code reviewers) will thank you.

Want to dive deeper? Check out the Laravel docs on helpers or explore other underrated gems like once(), rescue(), and transform(). Happy coding! 🚀