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

Laravel tap(): виконайте дію і поверніть те саме значення

Як використовувати tap() для побічної дії без втрати початкового значення, повертати модель після update(), застосовувати форму зі зворотним викликом, Tappable trait і не плутати tap() з перетворенням даних.

Іноді потрібно виконати дію над значенням, але після цього продовжити працювати саме з початковим значенням. Без tap() для цього часто з'являється окрема змінна, кілька рядків мутації і окремий return.

Laravel tap() робить цей намір явним: виконай побічну дію з цим значенням і поверни мені саме це значення назад.

Базовий сценарій: змінити модель і повернути її

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

Без tap() результатом виклику update() був би результат самого методу. Скорочена форма tap($user)->update(...) навмисно ігнорує це значення і повертає початковий об'єкт $user.

Тобто тут tap() потрібний не для коротшого синтаксису за будь-яку ціну, а щоб зберегти зрозумілий ланцюжок викликів і повернути модель після дії над нею.

Форма зі зворотним викликом показує основну логіку tap()

PHP - tap() зі зворотним викликом
$user = tap(    $user,    function (User $user) use ($name): void {        $user->name = $name;        $user->save();    },);

Перший аргумент передається у функцію зворотного виклику, а після її виконання tap() повертає початкове значення. Те, що повертає сама функція, не впливає на результат tap().

Результат функції зворотного виклику ігнорується

PHP - Результат функції не замінює початкове значення
$result = tap(    $user,    function (User $user): string {        $user->touch();         return 'ignored';    },); // $result is still the User instance.
tap() не є інструментом перетворення значення. Якщо вам потрібний результат функції зворотного виклику, поверніть його явно звичайним кодом або використайте API, призначений саме для перетворення.

tap() добре підходить для побічної дії

PHP - Зберегти модель і виконати супутню дію
return tap($order, function (Order $order): void {    $order->update([        'status' => OrderStatus::PAID,    ]);     Log::info('Order marked as paid', [        'order_id' => $order->id,    ]);});

Цей варіант корисний, коли основний результат методу - сама модель, а всередині потрібно виконати одну локальну побічну дію: зберегти зміни, записати діагностичну інформацію або підготувати об'єкт перед поверненням.

tap() корисний і без мутації

PHP - Перевірити значення і не ламати послідовність
$payload = tap(    $payload,    fn (array $payload) => Log::debug(        'Webhook payload',        ['payload' => $payload],    ),);

Функція зворотного виклику не зобов'язана змінювати значення. tap() зручний і для спостереження за проміжним станом, коли після журналювання або діагностики потрібно продовжити роботу з тим самим результатом.

Скорочена форма tap() прибирає зайву функцію для одного виклику методу

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

Коли функція зворотного виклику робить лише один виклик методу, Laravel дозволяє не передавати замикання. tap($value) повертає проксі-об'єкт, який викликає метод на початковому об'єкті, але після виклику знову повертає сам об'єкт.

Трейт Tappable додає tap() до власного класу

PHP - Власний клас з Tappable
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

PHP - Query Builder tap() для повторно використовуваної логіки запиту
DB::table('users')    ->tap(new ActiveUsersFilter())    ->orderBy('name')    ->get();

У сучасному Query Builder tap() використовується для застосування повторно використовуваної логіки запиту і завжди повертає builder. Це споріднена ідея - виконати дію та зберегти поточний ланцюжок - але це не те саме, що глобальна допоміжна функція tap($value).

tap() не робить операцію транзакційною

tap() керує тим, яке значення повертається після функції зворотного виклику або виклику методу. Він не додає транзакцію, не змінює правила збереження Eloquent і не робить кілька побічних дій атомарними.

Якщо функція зворотного виклику містить кілька записів у БД, які повинні виконатися разом, для цього все одно потрібна відповідна межа транзакції. tap() вирішує питання послідовності викликів, а не узгодженості даних.

Не приховуйте в 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;

Коротка допоміжна функція не повинна маскувати великий процес. Якщо функція зворотного виклику містить кілька суттєвих бізнес-кроків, окремий сервіс або метод із явною назвою зазвичай краще пояснить намір.

Коли tap() справді доречний

  • потрібно виконати дію над об'єктом і одразу повернути цей самий об'єкт;
  • потрібно вставити невелику побічну дію у вже зрозумілу послідовність;
  • потрібно подивитися або залогувати проміжне значення без зміни результату;
  • виклик методу повертає технічний результат, але зовнішньому коду потрібний початковий об'єкт;
  • невелика функція зворотного виклику робить код читабельнішим, а не ховає складність.

Коли звичайний код читабельніший

  • коли вам потрібне значення, яке повертає функція зворотного виклику;
  • коли результат має бути перетворений на інший тип або інший об'єкт;
  • коли всередині накопичується складний бізнес-процес;
  • коли окрема змінна і два очевидні рядки читаються краще за незнайому допоміжну функцію;
  • коли потрібна транзакція або інша гарантія виконання, яку tap() сам не надає.

Сенс tap() дуже вузький і саме тому корисний: виконай дію з цим значенням, але не змінюй те, що повертається назовні. Якщо цей намір не очевидний, звичайний код часто буде кращим.

#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