Railsで並べ替え可能なテーブルの実装手順

はじめに

Railsで作成したWebアプリにて、テーブルの中身を各カラムによって並べ替え可能にするために必要な手順をメモしておきます。テーブルの各カラム名をクリックすることで、並べ替えできるようになります。
以降に載せている手順はこちらのサイトに書いてある手順を参考にしています。

#228 Sortable Table Columns | RAILSCASTS


環境と前提

  • 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)を持つ


やりたいこと

以下のように、テーブルのカラム名をクリックした時にそのカラムを元にテーブル内容を並べ替えます。カラム名をクリックすると、昇順、降順が切り替わります。
sortable-table-url

この動作は、テーブルの各カラムに、「並び替えた状態のテーブルを含む同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_directionsort_columnapp/controllers/users_controller.rbにprivateメソッドとして追加します。

app/controllers/users_controller.rb
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_directionsort_columnの値に基いて並び替えたデータを取得するために、
以下のようにテーブルの中身となるモデルのデータ取得部分にorder(sort_column + ' ' + sort_direction)を追記します。

app/controllers/users_controller.rb
  def index
      @users = User.all.order(sort_column + ' ' + sort_direction)
  end

取得した値を並び替え(order/reorder) | Railsドキュメント

そして最後にHelperメソッドからsort_directionsort_columnを使用できるように以下の記述をControllerの一番上に追記しておきます。

app/controllers/users_controller.rb
helper_method :sort_column, :sort_direction

以上でControllerの修正は完了です。

Helperメソッドの追加

続いてapp/helpers/application_helper.rbに以下のsortableメソッドを追加します。

app/helpers/application_helper.rb
  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を使用するように修正します。

app/views/users/index.html.erb
  <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

この投稿へのコメント

  1. said on 2016年7月14日 at 16:29

    はじめまして、とても参考になる記事を有難うございます。

    続けて質問ですが、親モデルにネストされた子モデルがあり、
    親モデルのShowメソッド内で子モデルの一覧を表示した場合に
    ソート機能を作る方法を教えて頂けませんでしょうか。

    例えば、価格.comで店舗が親モデル、商品が子モデルとした場合、
    店舗ごとのページで商品の一覧を表示するようなイメージです。

    お忙しいところ恐れ入りますが、ご教示頂けますと幸いです。

  2. said on 2016年7月14日 at 16:30

    はじめまして、とても参考になる記事を有難うございます。

    続けて質問ですが、親モデルにネストされた子モデルがあり、
    親モデルのShowメソッド内で子モデルの一覧を表示した場合に
    ソート機能を作る方法を教えて頂けませんでしょうか。

    例えば、価格.comで店舗が親モデル、商品が子モデルとした場合、
    店舗ごとのページで商品の一覧を表示するようなイメージです。

    お忙しいところ恐れ入りますが、ご教示頂けますと幸いです。。

コメントを残す

メールアドレスが公開されることはありません。

この投稿へのトラックバック

  1. 技術系メモ書き said on 2017年8月22日 at 09:20

    […] このページを参考にさせてもらった […]

トラックバック URL