Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@hituziando

【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
6
Help us understand the problem. What are the problem?