Collectionの中でとても好きなメソッドは、groupBy()
です。Laravelが存在しなかった時代ではよくDBクエリから返ってくる1次元の配列をグループ化するため、汚いコードを書いて2次元の配列に変換していたものです。しかし、CollectionのgroupByがあるとコードがシンプルで綺麗に書けること書けること。
このような画面が欲しい
以下のようなデータがDBにあると仮定して、
mysql> select id, name, gender from users; +----+---------------+-----------+ | id | name | gender | +----+---------------+-----------+ | 1 | 山田 千代 | 無回答 | | 2 | 佐藤 健一 | 男 | | 3 | 加藤 篤司 | 無回答 | | 4 | 山口 花子 | 女 | +----+---------------+-----------+ 4 rows in set (0.01 sec)
このデータを性別でグループ化して、以下の画面の表示となるようにしたいのです。
DBレベルでのデータのグループ化
DBレベルでは、データをグループ化して例えば性別(gender)ごとのユーザー数とかを以下のように取得できます。
>>> User::groupBy('gender')->selectRaw('gender, count(*)')->get(); => Illuminate\Database\Eloquent\Collection {#3422 all: [ App\User {#4345 gender: "女", count(*): 1, }, App\User {#4348 gender: "無回答", count(*): 2, }, App\User {#4349 gender: "男", count(*): 1, }, ], }
ちなみに実行されたSQL文は、
>>> sql() => [ [ "query" => "select gender, count(*) from `users` group by `gender`", "bindings" => [], "time" => 0.56, ], ]
しかしこれでは先の欲しい画面のデータを表示するには、さらにそれぞれの性別において個々のDBレコードの情報取得が必要となります。つまり、以下のようなSQLの実行が3回必要となります。
>>> User::where('gender', '=', '男')->get(); => Illuminate\Database\Eloquent\Collection {#4345 all: [ App\User {#4348 id: 2, name: "佐藤 健一", gender: "男", ... }, ], }
しかし、これではトータル4回のSQL文の実行となってしまい効率的でありません。
Collectionでのグループ化
今度は、DBからレコードを全部取得してからデータをグループ化します。
>>> User::all()->groupBy('gender'); => Illuminate\Database\Eloquent\Collection {#4336 all: [ "無回答" => Illuminate\Database\Eloquent\Collection {#4120 all: [ App\User {#3392 id: 1, name: "山田 千代", ... }, App\User {#4361 id: 3, name: "加藤 篤司", ... }, ], }, "男" => Illuminate\Database\Eloquent\Collection {#4345 all: [ App\User {#4188 id: 2, name: "佐藤 健一", ... }, ], }, "女" => Illuminate\Database\Eloquent\Collection {#4276 all: [ App\User {#3403 id: 4, name: "山口 花子", ... }, ], }, ], }
DBレベルのグループ化と違って、DBから取得した後にCollectionのgroupBy()
を適用なので、データをグループ化するだけでなく個々のデータもコレクションにキープしてくれます。そして、実行されたSQL文をチェックしてみると以下のように1つのみです。
>>> sql() => [ [ "query" => "select * from `users`", "bindings" => [], "time" => 0.51, ], ]
コントローラの作成
CollectionのgroupByを使用して必要な構造データを取得できたところで、コントローラを作成です。
namespace App\Http\Controllers; use App\User; class UserController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { return view('users') ->with(['genders' => User::all()->groupBy('gender')]); } }
ブレードは以下のように、2つのループ(@foreach)を使い、グループのキーである性別と個々のレコードを分けて表示します。
@section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザー性別分け</div> <div class="card-body"> <table class="table"> <thead> <tr> <td class="col-1"></td> <td class="col-1">ID</td> <td>名前</td> </tr> </thead> <tbody> @foreach($genders as $gender => $users) <tr> <td colspan="3">{{ $gender }}</td> </tr> @foreach($users as $user) <tr> <td></td> <td class="text-right">{{ $user->id }}</td> <td>{{ $user->name }}</td> </tr> @endforeach @endforeach </tbody> </table> </div> </div> </div> </div> </div> @endsection
これでめでたし欲しかった画面の完成です。
メルマガ購読の申し込みはこちらから。