Help us understand the problem. What is going on with this article?

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

はじめに

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

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

実装方法

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を実現できた

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした