はじめに
この記事は、以下のやりたいことを実現するための方法を紹介しています。
検索結果をテーブルに表示し、
かつそのテーブルをカラムによってソート可能で
ページネーションにも対応させること
実装方法
Gemfile
ページネーションはwill_paginate gemを使います。Gemfileに以下を追記し、bundle installしてください。
gem "will_paginate", "~> 3.1.7"
Helper
以下のヘルパーを作ります。このヘルパーはこちらの記事で紹介されているものをベースに、検索パラメータを付けてSortリクエストできるようにしています。
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を使用していますが、本題とは関係ありません。
<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モデルに定義した検索メソッドです。
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
メソッドを生やしています。検索パラメータを引数に受け取り、任意の検索条件によってフィルタしたものを返します。
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
class Author < ApplicationRecord
has_many :articles
end
まとめ
上記方法により、検索結果を一覧表示し、ソートも可能でページネーションにも対応しているテーブルができます。