はじめに
レコードの一覧をグループ化して表示する際のページネーションについて、少し躓いたのでまとめます。
ページネーションはkaminariを使用しています。
普段のページネーション
例えば、全てのユーザーを取得して10件ずつ一覧に表示するようなページネーションは以下のようになります。
def index
page = params[:page] || 1
@users = User.all.page(page).per(10)
end
<table>
<thead>
<tr>
<th>ユーザー情報</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td>id:<%= user.id %></td>
<td>name:<%= user.name %></td>
</tr>
</tbody>
<% end %>
</table>
<div class="m_t_30">
<ul><%= paginate(@users) %></ul>
</div>
また、kaminariでは配列に対してもページネーションをさせることができます。
def index
page = params[:page] || 1
array = []
users = User.all
users.each do |user|
array.push({
id: user.id,
name: user.name
})
end
@paginated_array = Kaminari.paginate_array(array).page(params[:page]).per(10)
end
<table>
<thead>
<tr>
<th>ユーザー情報</th>
</tr>
</thead>
<tbody>
<% @paginated_array.each do |item| %>
<tr>
<td>id:<%= item[:id] %></td>
<td>name:<%= item[:name] %></td>
</tr>
</tbody>
<% end %>
</table>
<div class="m_t_30">
<ul><%= paginate(@paginated_array) %></ul>
</div>
つまずいた場面
レコードをグループ化して表示させたい場面でのページネーションをどう実装するかで迷いました。
例
例えば以下のように、ユーザーを一覧に表示する際に、各ユーザーの持つcountryカラムの値ごとにグループ化して一覧を表示させたい場面があります。
さらにこの画面の中でユーザーに対してページネーションをするための実装を想定します。
def index
# 全てのユーザーを取得し、countryカラムの文字コード順に並べ替える
users = User.all.order(country: "DESC").order(id: "ASC")
@users_hash = users.gruop_by {|user| user.country}
end
<table>
<thead>
<tr>
<th>国別一覧</th>
</tr>
</thead>
<tbody>
<% @users_hash.keys.each do |country| %>
<% users = @users_hash[country] %>
<tr>
<th><%= country %>グループ</th>
</tr>
<% users.each do |user| %>
<tr>
<td><%= user.name %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
グループ化するためにはgroup_byを使用します。
gruop_byを使用すると、
ブロックを評価した結果をキー、対応する要素の配列を値とするハッシュを返すことができます。
先ほど配列に対してページネーションをした際には、配列の中に各ユーザーごとのデータが格納されたハッシュを入れていました。
なので、users.length
とKaminari.paginate_array(array).length
の結果が一致します。(1ページあたりの項目数はデフォルトだと25なので、users.length < 25の場合)
しかし、今回の場合ハッシュのkey,valueを配列の値としてpaginate_arrayに入れると[{key1, [value1, value2]},{key2,[value3, value4, value5]}...]
のようになり、ハッシュのkeyと同様の数(配列の要素数)に対してページネーションがされることになります。
つまり、
def index
~~
略
array = [{key1, [value1, value2]},{key2,[value3, value4, value5]}...]
@paginated_array = Kaminari.paginate_array(array).page(params[:page]).per(10)
とすると、keyが10項目以上にならなければページネーションされないことになります。
今回のケースではvalueが10項目以上の場合にページネーションをさせたいので、欲しい出力は得られなません。
解決策
以下のように実装することで解決できました。
def index
page = params[:page] || 1
@users = User.all.order(country: "DESC").order(id: "ASC").page(page).per(15)
@users_hash = @users.group_by {|user| user.country}
end
<table>
<thead>
<tr>
<th>国別一覧</th>
</tr>
</thead>
<tbody>
<% @users_hash.keys.each do |country| %>
<% users = @users_hash[country] %>
<tr>
<th><%= country %>グループ</th>
</tr>
<% users.each do |user| %>
<tr>
<td><%= user.name %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<div class="m_t_30">
<ul><%= paginate(@users) %></ul>
</div>
上記の出力は次のようになり、目標としていた出力が得られました!