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
これでめでたし欲しかった画面の完成です。
メルマガ購読の申し込みはこちらから。