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

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

要点(TL;DR)

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

概要

tap はコレクションを引数に受け取るコールバックを一度呼び出し、その後元のコレクション(同一参照)を返します。ログ記録・タイミング計測・デバッグ出力・メトリクス送信など、チェーンを途切れさせずに副作用を挟みたい場面で使います。

構文 / シグネチャ

public function tap(callable $callback): static

引数(表)

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

使用例

最小例

<?php

use Illuminate\Support\Collection;

// 標準出力に要素数を出すだけ(純PHPで依存少なめ)
$nums = collect([10, 20, 30])
    ->tap(fn (Collection $c) => file_put_contents('php://stdout', "count={$c->count()}\n"));

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

実務例

<?php

use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;

// 非アクティブユーザーを取得して処理する前にメトリクスを記録
$users = User::query()
    ->where('active', false)
    ->get()
    ->tap(function (Collection $c) {
        Log::info('deactivate_candidate_count', ['count' => $c->count()]);
    });

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

よくある落とし穴・注意

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

代替・関連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])

テスト例(Pest)

<?php
use Illuminate\Support\Collection;

it('calls callback and returns same instance', function () {
    $called = false;
    $c = collect([1, 2, 3]);
    $same = $c->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)をフォローする