
Laravel tap(): виконайте дію і поверніть те саме значення
Як використовувати tap() для побічної дії без втрати початкового значення, повертати модель після update(), застосовувати форму зі зворотним викликом, Tappable trait і не плутати tap() з перетворенням даних.
Іноді потрібно виконати дію над значенням, але після цього продовжити працювати саме з початковим значенням. Без tap() для цього часто з'являється окрема змінна, кілька рядків мутації і окремий return.
Laravel tap() робить цей намір явним: виконай побічну дію з цим значенням і поверни мені саме це значення назад.
Базовий сценарій: змінити модель і повернути її
$user->update([ 'name' => $name, 'email' => $email,]); return $user;return tap($user)->update([ 'name' => $name, 'email' => $email,]);Без tap() результатом виклику update() був би результат самого методу. Скорочена форма tap($user)->update(...) навмисно ігнорує це значення і повертає початковий об'єкт $user.
Тобто тут tap() потрібний не для коротшого синтаксису за будь-яку ціну, а щоб зберегти зрозумілий ланцюжок викликів і повернути модель після дії над нею.
Форма зі зворотним викликом показує основну логіку tap()
$user = tap( $user, function (User $user) use ($name): void { $user->name = $name; $user->save(); },);Перший аргумент передається у функцію зворотного виклику, а після її виконання tap() повертає початкове значення. Те, що повертає сама функція, не впливає на результат tap().
Результат функції зворотного виклику ігнорується
$result = tap( $user, function (User $user): string { $user->touch(); return 'ignored'; },); // $result is still the User instance.tap() добре підходить для побічної дії
return tap($order, function (Order $order): void { $order->update([ 'status' => OrderStatus::PAID, ]); Log::info('Order marked as paid', [ 'order_id' => $order->id, ]);});Цей варіант корисний, коли основний результат методу - сама модель, а всередині потрібно виконати одну локальну побічну дію: зберегти зміни, записати діагностичну інформацію або підготувати об'єкт перед поверненням.
tap() корисний і без мутації
$payload = tap( $payload, fn (array $payload) => Log::debug( 'Webhook payload', ['payload' => $payload], ),);Функція зворотного виклику не зобов'язана змінювати значення. tap() зручний і для спостереження за проміжним станом, коли після журналювання або діагностики потрібно продовжити роботу з тим самим результатом.
Скорочена форма tap() прибирає зайву функцію для одного виклику методу
return tap( $user, function (User $user) use ($attributes): void { $user->update($attributes); },);return tap($user) ->update($attributes);Коли функція зворотного виклику робить лише один виклик методу, Laravel дозволяє не передавати замикання. tap($value) повертає проксі-об'єкт, який викликає метод на початковому об'єкті, але після виклику знову повертає сам об'єкт.
Трейт Tappable додає tap() до власного класу
use Illuminate\Support\Traits\Tappable; final class Report{ use Tappable;} return $report->tap( function (Report $report): void { $report->prepare(); },);Якщо такий стиль природний для власного об'єкта, Laravel має трейт Tappable. Його tap() передає поточний екземпляр у замикання і після цього повертає той самий екземпляр.
Query Builder також має tap(), але це окремий API
DB::table('users') ->tap(new ActiveUsersFilter()) ->orderBy('name') ->get();У сучасному Query Builder tap() використовується для застосування повторно використовуваної логіки запиту і завжди повертає builder. Це споріднена ідея - виконати дію та зберегти поточний ланцюжок - але це не те саме, що глобальна допоміжна функція tap($value).
tap() не робить операцію транзакційною
Якщо функція зворотного виклику містить кілька записів у БД, які повинні виконатися разом, для цього все одно потрібна відповідна межа транзакції. tap() вирішує питання послідовності викликів, а не узгодженості даних.
Не приховуйте в tap() великий бізнес-процес
return tap($order, function (Order $order): void { $order->reserveStock(); $order->chargeCustomer(); $order->generateInvoice(); $order->notifyWarehouse(); $order->dispatchShipment();});$order = $checkoutService->complete($order); return $order;Коротка допоміжна функція не повинна маскувати великий процес. Якщо функція зворотного виклику містить кілька суттєвих бізнес-кроків, окремий сервіс або метод із явною назвою зазвичай краще пояснить намір.
Коли tap() справді доречний
- потрібно виконати дію над об'єктом і одразу повернути цей самий об'єкт;
- потрібно вставити невелику побічну дію у вже зрозумілу послідовність;
- потрібно подивитися або залогувати проміжне значення без зміни результату;
- виклик методу повертає технічний результат, але зовнішньому коду потрібний початковий об'єкт;
- невелика функція зворотного виклику робить код читабельнішим, а не ховає складність.
Коли звичайний код читабельніший
- коли вам потрібне значення, яке повертає функція зворотного виклику;
- коли результат має бути перетворений на інший тип або інший об'єкт;
- коли всередині накопичується складний бізнес-процес;
- коли окрема змінна і два очевидні рядки читаються краще за незнайому допоміжну функцію;
- коли потрібна транзакція або інша гарантія виконання, яку
tap()сам не надає.
Сенс tap() дуже вузький і саме тому корисний: виконай дію з цим значенням, але не змінюй те, що повертається назовні. Якщо цей намір не очевидний, звичайний код часто буде кращим.




