- カテゴリ: 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\Collection | Collectionインスタンス | コレクションチェーンの途中でログ・デバッグ・計測を挟む |
どちらも「元の値を変えずに返す」という点で共通です。値を変換したい場合は pipe() を使います。
要点(TL;DR)
- チェーンの途中で「ログを書く」「計測する」など副作用を実行し、同じコレクションを返す。
- 最低限の使い方:
$c = collect([1,2,3])->tap(fn ($c) => Log::info('count', ['n' => $c->count()])); - 罠:コールバックの戻り値は捨てられる/変換したいなら
pipeやmap/内部で要素を直接変更すると破壊的。
グローバルヘルパ 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
引数(表)
| 引数 | 型 | 必須 | 既定値 | 説明 |
|---|---|---|---|---|
$callback | callable(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 を使用 |
参考リンク
- Laravel 12 ドキュメント「Collections」:https://laravel.com/docs/12.x/collections
- Laravel 12 ドキュメント「Helpers: tap」:https://laravel.com/docs/12.x/helpers#method-tap
- フレームワーク実装(Collection)ソース:https://github.com/laravel/framework/blob/12.x/src/Illuminate/Support/Collection.php
- フレームワーク実装(LazyCollection)ソース:https://github.com/laravel/framework/blob/12.x/src/Illuminate/Support/LazyCollection.php
