はじめに
Railsで作成したWebアプリにて、テーブルの中身を各カラムによって並べ替え可能にするために必要な手順をメモしておきます。テーブルの各カラム名をクリックすることで、並べ替えできるようになります。
以降に載せている手順はこちらのサイトに書いてある手順を参考にしています。
環境と前提
- Rails 4.2.0
- Ruby 2.2.0
- 以降では、並び替えたいテーブルがあるページのURLは
192.168.33.11:3000/users
192.168.33.11:3000/users
にあるテーブルはName、E-mailカラム(データベース上のテーブルのカラム名はname、email)を持つ
やりたいこと
以下のように、テーブルのカラム名をクリックした時にそのカラムを元にテーブル内容を並べ替えます。カラム名をクリックすると、昇順、降順が切り替わります。
この動作は、テーブルの各カラムに、「並び替えた状態のテーブルを含む同URLへのリンク」を設定することで機能します。具体的には、今回のように並び替えたいテーブルがあるページのURLが192.168.33.11:3000/users
の場合は、カラム名の「Name」にはhttp://192.168.33.11:3000/users?direction=desc&sort=name
というリンクを設定しています。このリンクをみてわかるように、URLにdirection=並び替え順&sort=並び替え元となるカラム名
という形でURLクエリパラメータを付与しています。これによって並び替え順と並び替え元なるカラム名を指定しています。
そしてテーブルの各カラムに「並び替えた状態のテーブルを含む同URLへのリンク」を設定するためにHelperにメソッドを追加します。また、ControllerとViewをHelperに追加するメソッドに合わせて修正します。以降でController、Helper、Viewそれぞれの修正についてメモします。
Controllerの修正
はじめに並べ替え順(asc、desc)と並べ替え元となるカラム名のデフォルト値を返し、また値が正常であるかを確認するためのメソッド、sort_direction
とsort_column
をapp/controllers/users_controller.rb
にprivateメソッドとして追加します。
private
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
end
def sort_column
User.column_names.include?(params[:sort]) ? params[:sort] : "name"
end
どちらのメソッドもSQLインジェクションを防ぐために受け取ったパラメータが正常であるかを確認しています。具体的には、sort_direction
ではViewから受け取るparams[:direction]
がasc
、もしくはdesc
のどちらかに含まれるかを三項演算子を使用して判定しています。params[:direction]
が指定されていない場合はasc
となります。すなわちデフォルトの状態ではasc
で並び替えられたテーブルが表示されます。
sort_column
においても同様に、Viewから受け取るparams[:sort]
が実際に存在するカラム名であるかを三項演算子を使用して判定しています。params[:sort]
が指定されていない場合はname
で並び替えます。すなわちデフォルトの状態ではname
カラムで並び替えられたテーブルが表示されます。
上記のsort_direction
、sort_column
の値に基いて並び替えたデータを取得するために、
以下のようにテーブルの中身となるモデルのデータ取得部分にorder(sort_column + ' ' + sort_direction)
を追記します。
def index
@users = User.all.order(sort_column + ' ' + sort_direction)
end
そして最後にHelperメソッドからsort_direction
、sort_column
を使用できるように以下の記述をControllerの一番上に追記しておきます。
helper_method :sort_column, :sort_direction
以上でControllerの修正は完了です。
Helperメソッドの追加
続いてapp/helpers/application_helper.rb
に以下のsortable
メソッドを追加します。
def sortable(column, title = nil)
title ||= column.titleize
css_class = (column == sort_column) ? "current #{sort_direction}" : nil
direction = (column == sort_column && sort_direction == "asc") ? "desc" : "asc"
link_to title, {:sort => column, :direction => direction}, {:class => css_class}
end
sortable
は、引数を2つ受けます。第1引数は並び替え元となるデータベース上のテーブルのカラム名、第2引数はViewに表示するカラム名です。もしデータベース上のテーブルのカラム名とViewに表示するカラム名が同じでよければ、第2引数は省略可能です。実際の使用例は以降にメモしています。
なお、css_class = (column == sort_column) ? "current #{sort_direction}" : nil
では、並べ替え元のカラム名のリンクのcssクラスとして、並び替え順に合わせてcurrent asc
またはcurrent desc
を付与するようにしています。このcssクラスを好みで修正し、例えば上三角画像や下三角画像を表示するようにすれば、現在どのカラム名で昇順、降順どちらで並び替えているかがわかりやすくなると思います。詳しくは冒頭に載せた参考URLに載っています。
上記のメソッドの理解に参考になるサイトを以下にメモしておきます。
Ruby の自己代入 x ||= 1 の謎 | Qiita
ActiveSupport | String#titleize | ActiveSupport | String#titleize
Railsでaタグのリンクを作成するlink_toメソッドの使い方 | Rails Webook
Viewの編集
最後にViewのテーブルのカラム名部分に上で追加したsortable
を使用するように修正します。
<p id="notice"><%= notice %></p>
<h1>Listing Users</h1>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th><%= sortable "name", "Name" %></th>
<th><%= sortable "email", "E-mail" %></th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to 'New User', new_user_path %>
上記のように、<%= sortable "name", "Name" %>
、<%= sortable "email", "E-mail" %>
でsortable
メソッドを使用しています。<%= sortable "name", "Name" %>
の"name"
がデータベース上のカラム名、"Name"
がViewで表示されるカラム名になります。もし第2引数の"Name"
を省略した場合は、Viewで表示されるカラム名も"name"
になります。
以上で完了です。もし間違いやより良い方法などがありましたら教えて頂ければと思います。
SPONSORED LINK
はじめまして、とても参考になる記事を有難うございます。
続けて質問ですが、親モデルにネストされた子モデルがあり、
親モデルのShowメソッド内で子モデルの一覧を表示した場合に
ソート機能を作る方法を教えて頂けませんでしょうか。
例えば、価格.comで店舗が親モデル、商品が子モデルとした場合、
店舗ごとのページで商品の一覧を表示するようなイメージです。
お忙しいところ恐れ入りますが、ご教示頂けますと幸いです。
はじめまして、とても参考になる記事を有難うございます。
続けて質問ですが、親モデルにネストされた子モデルがあり、
親モデルのShowメソッド内で子モデルの一覧を表示した場合に
ソート機能を作る方法を教えて頂けませんでしょうか。
例えば、価格.comで店舗が親モデル、商品が子モデルとした場合、
店舗ごとのページで商品の一覧を表示するようなイメージです。
お忙しいところ恐れ入りますが、ご教示頂けますと幸いです。。