LoginSignup
6

More than 3 years have passed since last update.

【Rails】検索結果をテーブルに表示し、かつそのテーブルをカラムによってソート可能でページネーションにも対応させる

Last updated at Posted at 2019-08-24

はじめに

この記事は、以下のやりたいことを実現するための方法を紹介しています。

検索結果をテーブルに表示し、
かつそのテーブルをカラムによってソート可能で
ページネーションにも対応させること

実装方法

Gemfile

ページネーションはwill_paginate gemを使います。Gemfileに以下を追記し、bundle installしてください。

Gemfile
gem "will_paginate", "~> 3.1.7"

Helper

以下のヘルパーを作ります。このヘルパーはこちらの記事で紹介されているものをベースに、検索パラメータを付けてSortリクエストできるようにしています。

app/helpers/search_helper.rb
module SearchHelper

  def sort_asc(column_to_be_sorted, search_params = nil)
    opts = { :column => column_to_be_sorted, :direction => "asc" }
    opts.merge!(search_params) if search_params.present?
    link_to "▲", opts
  end

  def sort_desc(column_to_be_sorted, search_params = nil)
    opts = { :column => column_to_be_sorted, :direction => "desc" }
    opts.merge!(search_params) if search_params.present?
    link_to "▼", opts
  end

  def sort_direction
    %W[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
  end
end

View

以下は適当な検索フォームとテーブルを実装したViewです。
例は、著者と記事の1対多の関係で、記事一覧を表示するものです。ちなみに、スタイルはBootstrapを使用していますが、本題とは関係ありません。

app/views/articles/index.html.erb
<div class="container">
  <!-- 検索フォーム -->
  <%= form_with url: articles_path, method: :get, local: true do |form| %>
    <div class="row mb-3">
      <div class="input-group col">
        <div class="input-group-prepend">
          <span class="input-group-text">タイトル</span>
        </div>
        <%= form.text_field :title, class: "form-control", value: @search_params[:title] %>
      </div>
      <div class="input-group col">
        <div class="input-group-prepend">
          <span class="input-group-text">著者名</span>
        </div>
        <%= form.text_field :author, class: "form-control", value: @search_params[:author] %>
      </div>
    </div>
    <div class="row mb-3">
      <div class="input-group col">
        <div class="input-group-prepend">
          <span class="input-group-text">発刊年</span>
        </div>
        <%= form.text_field :publication_year_from, class: "form-control", 
                                                    value: @search_params[:publication_year_from] %>
        <div class="input-group-prepend">
          <span class="input-group-text">~</span>
        </div>
        <%= form.text_field :publication_year_to, class: "form-control", 
                                                  value: @search_params[:publication_year_to] %>
      </div>
    </div>
    <div class="form-group row">
      <div class="col">
        <%= form.submit '検索', class: 'btn btn-primary pull-right' %>
      </div>
    </div>
  <% end %>

  <!-- テーブル -->
  <%= will_paginate @articles %>
  <table class="table">
    <thead>
    <tr>
      <th scope="col">ID
        <%= sort_asc('id', @search_params) %>
        <%= sort_desc('id', @search_params) %>
      </th>
      <th scope="col">タイトル
        <%= sort_asc('title', @search_params) %>
        <%= sort_desc('title', @search_params) %>
      </th>
      <th scope="col">著者名
        <%= sort_asc('authors.name', @search_params) %>
        <%= sort_desc('authors.name', @search_params) %>
      </th>
      <th scope="col">発刊年
        <%= sort_asc('publication_year', @search_params) %>
        <%= sort_desc('publication_year', @search_params) %>
      </th>
    </tr>
    </thead>
    <tbody>
    <% @articles.each do |it| %>
      <tr>
        <th scope="row"><%= it.id %></th>
        <td><%= it.title %></td>
        <td><%= it.author.name %></td>
        <td><%= it.publication_year %></td>
      </tr>
    <% end %>
    </tbody>
  </table>
</div>

sort_asc, sort_descメソッドの第1引数には、ソートのキーにしたいカラム名を指定します。著者名だけ少し違いますが、理由は後述します。

Controller

Articleモデルをorder, paginate, searchのメソッドチェーンにより絞り込んだものを@articles変数に格納しています。searchメソッドはArticleモデルに定義した検索メソッドです。

app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  include SearchHelper

  def index
    sort_column    = params[:column].presence || 'id'
    @articles      = Article.joins(:author).search(search_params)
                            .order(sort_column + ' ' + sort_direction)
                            .paginate(page: params[:page], per_page: 10)
    @search_params = search_params
  end

   def search_params
    params.permit(:title, :author, :publication_year_from, :publication_year_to)
  end
end

Articleに関連するAuthorのnameでソートしたい場合は、

Article.joins(:author).order('authors.name asc')

とすることで可能です。
Viewの著者名でソートする部分でsort_asc, sort_descメソッドにauthors.nameを指定しているのはこのためです。

Model

Articleモデルにsearchメソッドを生やしています。検索パラメータを引数に受け取り、任意の検索条件によってフィルタしたものを返します。

app/models/article.rb
class Article < ApplicationRecord
  belongs_to :author

  def self.search(params)
    title       = params[:title]
    author_name = params[:author]
    year_from   = params[:publication_year_from]
    year_to     = params[:publication_year_to]
    @articles   = Article.all
    if author_name.present?
      author = Author.find_by(name: author_name)
      @articles = @articles.where(author_id: author.id) if author.present?
    end
    @articles = @articles.where("publication_year >= ?", year_from) if year_from.present?
    @articles = @articles.where("publication_year <= ?", year_to) if year_to.present?
    @articles
  end
end
app/models/author.rb
class Author < ApplicationRecord
  has_many :articles
end

まとめ

上記方法により、検索結果を一覧表示し、ソートも可能でページネーションにも対応しているテーブルができます。

参考

RailsでテーブルのSortかつPaginationを実現できた

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6