tap — コレクションに対する副作用を挟んで同じインスタンスを返す

未分類
  • カテゴリ: collection
  • 掲載バージョン: Laravel 12・PHP 8.4
  • 名前空間 / FQCN / コマンド: Illuminate\Support\Collection::tap
  • 関連: each / pipe / when / unless / tap(ヘルパ)
  • 変更履歴: —

Laravel の tap() とは?

tap() は「値を受け取り・副作用を実行し・元の値をそのまま返す」関数です。Laravelには2種類の tap() があります。

種類場所対象用途
グローバルヘルパ tap($value, $callback)Illuminate\Support\helpers.php任意の値・オブジェクトメソッドチェーンを崩さずにどんな値にも副作用を挟む
Collectionメソッド ->tap($callback)Illuminate\Support\CollectionCollectionインスタンスコレクションチェーンの途中でログ・デバッグ・計測を挟む

どちらも「元の値を変えずに返す」という点で共通です。値を変換したい場合は pipe() を使います。

要点(TL;DR)

  • チェーンの途中で「ログを書く」「計測する」など副作用を実行し、同じコレクションを返す。
  • 最低限の使い方:$c = collect([1,2,3])->tap(fn ($c) => Log::info('count', ['n' => $c->count()]));
  • 罠:コールバックの戻り値は捨てられる変換したいなら pipemap内部で要素を直接変更すると破壊的

グローバルヘルパ tap() の使い方

グローバルヘルパ tap($value, $callback) は、任意の値に対してコールバックを実行し、元の値を返します。コレクション以外でも使えます。

シグネチャ

function tap($value, callable $callback = null)

基本的な使い方

 'Alice']), function (User $user) {
    $user->save();
});

// $user はsave済みのUserインスタンス
echo $user->id; // 保存後のID

// コールバック省略でメソッドの戻り値を無視する(流暢なチェーン)
$user = tap(User::find(1))->update(['name' => 'Bob']);
// update() は bool を返すが、tap()でWrapするとUserインスタンスを得られる

デバッグ・ログ出力での活用

validated()),
    fn (Order $order) => Log::info('Order created', ['id' => $order->id, 'total' => $order->total])
);

// $result はOrderインスタンス(create()の戻り値そのまま)

Collectionメソッド ->tap() の使い方

CollectionのメソッドとしてのAの tap() は、コレクションチェーンの途中に副作用を差し込みます。コールバックにはコレクション自身が渡され、処理後は同一インスタンスが返ります。

構文 / シグネチャ

public function tap(callable $callback): static

引数(表)

引数必須既定値説明
$callbackcallable(Collection $collection): voidはいコレクションを受け取り、副作用を実行。戻り値は無視される
  • 戻り値static同じインスタンスを返す)
  • 例外/副作用:コールバック内で投げられた例外がそのまま伝播。I/O(ログ/DB/外部API)等の副作用はコールバックに依存。

使用例

最小例

tap(fn (Collection $c) => file_put_contents('php://stdout', "count={$c->count()}\n"));

// $nums は [10, 20, 30] のまま(同一インスタンス)

ログ出力例

where('active', false)
    ->get()
    ->tap(function (Collection $c) {
        Log::info('deactivate_candidate_count', ['count' => $c->count()]);
    });

// 以降のチェーンはそのまま継続可能
$users->each->update(['active' => true]);

デバッグ出力例(dd / dump)

filter(fn ($n) => $n > 2)
    ->tap(fn ($c) => dump($c->values()->all()))  // ここで [3, 4, 5] を確認
    ->map(fn ($n) => $n * 10)
    ->values();

// $result は [30, 40, 50]

メソッドチェーンでの計測例

filter(fn ($n) => $n % 2 === 0)
    ->tap(function ($c) use ($startTime) {
        Log::debug('filter elapsed', [
            'ms' => round((microtime(true) - $startTime) * 1000, 2),
            'count' => $c->count(),
        ]);
    })
    ->map(fn ($n) => $n ** 2)
    ->take(5);

変更を加えつつ元の値を返す例(Eloquentモデル更新)

get()
    ->tap(function ($collection) {
        // 各モデルのステータスを更新(コレクション構造は変わらない)
        $collection->each->update(['reviewed_at' => now()]);
    })
    ->sortBy('price')
    ->values();

よくある落とし穴・注意

  • 戻り値は無視tap(fn () => 123)123 は使われません。形を変えたいなら pipe(チェーンの値を差し替える)、要素変換は map
  • 非破壊だが中で破壊し得るtap 自体は非破壊。コールバックで要素を参照変更すれば結果は変わります。意図しない変更に注意。
  • 例外はそのまま伝播:ログ送信やAPI呼び出しを入れる場合は失敗ハンドリングを。
  • パフォーマンス:O(1)。反復処理は行いません。
  • キー保持:影響なし(そのまま)。

tap() vs pipe() vs each() の違い

メソッド戻り値コールバックの引数主な用途典型的なユースケース
tap($callback)元のコレクション(同一インスタンス)Collectionインスタンス副作用・観測ログ出力、デバッグ、計測
pipe($callback)コールバックの戻り値(何でも可)Collectionインスタンス変換・集約コレクションを別の型に変換する
each($callback)元のコレクション(同一インスタンス)各要素・キー各要素への副作用各要素にログ出力・更新処理
tap(fn ($c) => dump($c->count())); // Collection([1,2,3])

// pipe: コールバックの戻り値が返る
$count = $collection->pipe(fn ($c) => $c->count()); // 3(整数)

// each: 各要素ごとにコールバックが呼ばれ、コレクションが返る
$same2 = $collection->each(fn ($item) => dump($item)); // Collection([1,2,3])

代替・関連APIとの比較

  • each:各要素に対して副作用を実行。ループが必要なときはこちら。
  • pipe:コレクションをコールバックへ渡し、コールバックの戻り値でチェーンの値を差し替える
  • when / unless:条件に応じてサブチェーンを実行。
  • tap(グローバルヘルパ):任意の値に対して同様のパターンを適用。コレクション以外の変数にも使える。

選定基準

  • 副作用だけして元のコレクションで続けたい」→ tap
  • 変換結果で続けたい」→ pipe
  • 各要素に対する副作用」→ each
  • 条件付きでチェーンを分岐」→ when / unless

コレクション固有メモ

  • チェーン可否:可
  • 破壊的/非破壊:非破壊(ただしコールバック内で破壊可能)
  • キー保持:保持
  • LazyCollection 対応:可(同じく O(1)、コールバックは即時実行)
  • 計算量の目安:O(1)

入出力対応(サンプル)

入力tap 内の副作用出力
collect(['a' => 1, 'b' => 2])Log::info('count', ['n' => 2])同じ Collection(['a' => 1, 'b' => 2])

よくある質問(FAQ)

Laravelのtapとは?

tap() は「値に副作用を挟んで元の値を返す」パターンを実現する関数・メソッドです。Laravelには tap($value, $callback) というグローバルヘルパと、Collection::tap($callback) というコレクションメソッドの2種類があります。どちらもコールバック内で副作用(ログ・更新・デバッグ)を実行し、元の値・コレクションをそのまま返します。

tapはいつ使う?

以下の場面で活用できます:

  • デバッグ:コレクションチェーンの途中で dump()dd() を挟んで値を確認したい
  • ログ出力:処理件数や中間値をログに残したい
  • 計測:処理時間をチェーンを壊さずに測定したい
  • 副作用の挿入:チェーン内でモデルの保存・通知送信などを行いたい(ただしエラーハンドリングに注意)
  • メソッドの戻り値を無視したいtap($model)->update([...]) のように bool を返すメソッドを呼びながらモデルインスタンスを得たい

tapとpipeの違いは?

最大の違いは戻り値です:

  • tap()元の値・コレクションをそのまま返す。コールバックの戻り値は無視される。
  • pipe()コールバックの戻り値を返す。コレクションを別の型(整数・文字列・配列)に変換したい場合に使う。
tap(fn ($c) => Log::info($c->count())); // Collection

// pipe: コールバックの戻り値が返る(変換)
$count = $collection->pipe(fn ($c) => $c->count()); // 3(int)

テスト例(Pest)

tap(function (Collection $x) use (&$called, $c) {
        expect($x)->toBe($c);
        $called = true;
    });

    expect($called)->toBeTrue();
    expect($same)->toBe($c); // 同一インスタンス
});

トラブルシュート(エラー別)

症状/エラー原因対処
Call to undefined function collect()フレームワーク外/オートロード未設定Laravel 環境で実行 or illuminate/support を読み込む
Class "Log" not foundファサード未インポート or 設定未了use Illuminate\Support\Facades\Log; を追加、ロギング設定を確認
副作用が実行されないコールバック未実行箇所(早期 return 等)tap の位置を見直し、実行経路を確認
期待した変換が反映されないtap は戻り値を無視変換は pipe / map を使用

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする