1. 概要
Rails の投稿一覧ページに「並び替え」機能を追加します。
- プルダウンで 新しい順 / 名前順 / ランダム順 を選択
- ボタンで 昇順⇄降順を切り替え
- 将来、いいね数順やコメント数順などのカスタムソートも簡単に追加可能
2. コントローラを編集
app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
# ======== ここから ========
@sort = params[:sort].presence_in(%w[new name random]) || "new"
@dir = params[:dir].presence_in(%w[asc desc]) || "desc"
base = Post.all
@posts =
case @sort
# 新しい順 / 古い順
when "new"
base.order(created_at: @dir)
# タイトルの昇順 / 降順
when "name"
base.order(title: @dir, created_at: :desc)
# ランダム(DBごとに関数が異なる)
when "random"
adapter = ActiveRecord::Base.connection.adapter_name
func = adapter =~ /mysql/i ? "RAND()" : "RANDOM()"
base.order(Arel.sql(func))
else
base.order(created_at: :desc)
end
end
# ======== ここまで ========
end
コードの解説
1. 並び替え方法を決める
@sort = params[:sort].presence_in(%w[new name random]) || "new"`
-
params[:sort]→ URL から「どんな並び順にしたいか」の情報を受け取る- 例:
/posts?sort=nameとアクセスされたら"name"が入る
- 例:
-
.presence_in(%w[new name random])- 「new」「name」「random」の中に含まれているかチェック
- それ以外の変な値なら
nilを返す
-
|| "new"-
nilだったときの保険で"new"(新しい順)をデフォルトにする
-
2. 並び替えを実行する
@posts =
case @sort
when "new"
base.order(created_at: @dir)
case @sort- 決めた並び替えの種類に応じて処理を分ける
-
when "new"- 並び替え方法が
"new"(新しい順)の場合
- 並び替え方法が
-
base.order(created_at: @dir)- 投稿を作成日時(
created_at)で並べ替える -
@dirが"desc"→ 新しい順 -
@dirが"asc"→ 古い順
- 投稿を作成日時(
3. ビューを編集
app/views/posts/index.html.erb
<div class="sort-toolbar">
<%= form_with url: posts_path, method: :get, local: true do %>
<label>並び替え:</label>
<%= select_tag :sort,
options_for_select([["新しい順","new"],
["名前順","name"],
["ランダム","random"]], @sort),
onchange: "this.form.submit()" %>
<%= hidden_field_tag :dir, @dir %>
<noscript><%= submit_tag "適用" %></noscript>
<% end %>
<% next_dir = (@dir == "asc" ? "desc" : "asc") %>
<%= link_to (@dir == "asc" ? "昇順 → 降順" : "降順 → 昇順"),
url_for(params.permit(:sort, :page).merge(dir: next_dir)),
class: "btn-toggle" %>
</div>
1. 全体の役割
この <div class="sort-toolbar"> は、
- プルダウン(select)で どの種類で並べ替えるか を選ぶ
- ボタン(link_to)で 昇順⇄降順を切り替える
2. プルダウン部分
<%= form_with url: posts_path, method: :get, local: true do %>
<label>並び替え:</label>
<%= select_tag :sort,
options_for_select([["新しい順","new"],
["名前順","name"],
["ランダム","random"]], @sort),
onchange: "this.form.submit()" %>
<%= hidden_field_tag :dir, @dir %>
<noscript><%= submit_tag "適用" %></noscript>
<% end %>
-
form_with url: posts_path, method: :get
→ 投稿一覧ページに GETリクエスト を送るフォーム -
select_tag :sort, options_for_select(...)
→ プルダウンを作成-
["新しい順","new"]→ 表示は「新しい順」、送信される値は"new" -
@sortが現在選ばれている値
-
-
onchange: "this.form.submit()"
→ プルダウンを変更した瞬間にフォームを送信(自動でリロード) -
hidden_field_tag :dir, @dir
→ いまの「昇順/降順」も一緒に送信して保持するための隠しフィールド -
<noscript>
→ JavaScript が無効なとき用に「適用ボタン」を表示
3. 昇順⇄降順の切替ボタン
<% next_dir = (@dir == "asc" ? "desc" : "asc") %>
<%= link_to (@dir == "asc" ? "昇順 → 降順" : "降順 → 昇順"),
url_for(params.permit(:sort, :page).merge(dir: next_dir)),
class: "btn-toggle" %>
-
next_dir = (@dir == "asc" ? "desc" : "asc")
→ 今が昇順なら「次は降順」、今が降順なら「次は昇順」をセット -
link_to
→ 表示ラベルは「昇順 → 降順」または「降順 → 昇順」
→ クリックすると URL に?dir=ascまたは?dir=descを付けてアクセス -
url_for(params.permit(:sort, :page).merge(dir: next_dir))
→ いまのsortとpageを引き継ぎつつ、dirだけ切り替える
4. 並び替えを追加する方法
1. コントローラ側にロジックを追加
app/controllers/posts_controller.rb
when "name"
base.order(title: @dir, created_at: :desc)
# ======== ここに追加! =========
when "random"
adapter = ActiveRecord::Base.connection.adapter_name
例: いいね数順
app/controllers/posts_controller.rb
when "likes"
base.order(likes_count: @dir)
例: コメント数順
app/controllers/posts_controller.rb
when "comments"
base.left_joins(:comments).group(:id).order("COUNT(comments.id) #{@dir.upcase}")
例: ユーザー名順(ログインなしでもOK)
app/controllers/posts_controller.rb
when "user_name"
base.joins(:user).order("users.name #{@dir.upcase}")
2. ビュー(プルダウンの項目に追加)
app/views/posts/index.html.erb
<%= select_tag :sort,
options_for_select([
["新しい順","new"],
["名前順","name"],
# ======== ここに追加! ========
["ランダム","random"]
], @sort),
onchange: "this.form.submit()" %>