前回firstOrCreate()について説明した際に、tap()について言及しました。

tap
はIlluminate\Support\helpers.php
で定義されているヘルパー関数で、Laravelの生みの親のTaylor Otwell氏がtap()について解説しているように、簡潔なコードを書くのに役立つ便利な関数です。
今回はそんなtap関数の動作や、使い方について紹介したいと思います。
※使用したLaravelのバージョンは5.6です。
解説
tap()の使用例
まず最初にtap()の使用例を示します。
$model = tap(Post::findOrFail(1), function ($post) {
$post->title = 'How to use tap function';
});
上記の例は、findOrFail()を使用してDBを検索、Postモデルのインスタンスを取得し、そのtitleを書き換えています。
tap()は第2引数にクロージャを受け取り、そのクロージャーの引数には、tap()の第1引数が渡されます。
つまり、$postには、Post::findOrFail()の結果がセットされます。
そして、クロージャー内の処理を実行した後、$postが返却されるのです。
クロージャーに引数で渡した値以外をreturn出来ない点に注意下さい。
このサンプルでのtap()のメリットは、下記の2点が挙げられます。
- findOrFail()の結果を、一時変数を作成して格納しなくて良い
- クロージャーのスコープ内で処理が完結している
tap()の内部で実行した結果は、$modelに代入する以外に影響を及ぼさないようになっています。
また、tap()の戻り値はtap()の第1引数に設定した変数となるため、この例ではPostモデルのインスタンスが返却されます。
そのため、下記のように、メソッドチェーンで更に別の処理を実行することが可能です。
$array = tap(Post::findOrFail(1), function ($post) {
$post->title = 'Title changed';
})->toArray();
tap()を使ってfindOrCreate()を実装してみる
Illuminate\Database\Eloquent\Builderクラスでは、既にfirstOrCreate()が実装されていますので、tap()を使って、find()とcreate()を行うfindOrCreate()を新たに追加してみたいと思います。
Builderクラスに機能を追加するために、Laravelのマクロ機能を利用します。
マクロについては別の記事で紹介していますので、詳細はそちらを参照下さい。

マクロを登録するためのサービスプロバイダーを作成したら、下記のようにサービスプロバイダーを実装します。
find($id))) {
return $instance;
}
return tap($this->newModelInstance($attributes), function ($instance) use ($id) {
$instance->save();
});
});
}
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
}
そして、config/app.phpにサービスプロバイダーを登録します。
'providers' => [
(中略)
App\Providers\MacroServiceProvider::class,
],
以上で実装は完了です。
追加したfindOrCreate()は下記のように使用します。
$post = Post::findOrCreate(1, [
'title' => 'findOrCreate()を追加',
'user_id' => 1,
'content' => 'Content about tap helper function...',
'is_public' => true,
]);
おわりに
今回はtap()の使い方と、tap()を使ったメソッドの実装例を紹介しました。
tap()はスコープ内に処理を閉じ込めたり、メソッドチェーンを組み込んだりと、今回紹介した以外にも様々なシーンで活用できる可能性があるメソッドなので、他にも効果的な使い方を探したいと思います。