はじめに
今回はタスク管理アプリにデータの検索・ソート機能を追加します。
また、その他にscopeやフィルタの追加やURLを自動でリンクとして表示できるように変更します。
データの絞り込み
Railsにはデータベースからデータを検索するための機能群が用意されています。
また、更新・削除時に更新対象の条件の絞り込みも同じやり方を使えます。
データを絞り込んで検索を行う場合、以下の3点からコードを組み立てます。
絞り込み条件は付けない場合もあります。
- 起点
- 絞り込み条件
- 実行部分
以下のコードではadminカラムにtrueが入っているUserのうち最初に見つかったものを取得します。
User.where(admin:true).first
このコードを上記の3つの部分に分解すると以下のようになります。
- User…①起点
- where(admin:true)…②絞り込み条件
- first…③実行部分
絞り込みの起点
検索・更新のコードを書き出すスタート地点です。基本的には処理対象のモデルのクラスが起点となります。
user.tasksのようなhas_many関連を起点にすることもできます。この場合、Taskを起点として絞り込むのと同じ効果となります。
絞り込み条件
起点に対して絞り込みの条件を追加する部分です。以下のようなクエリー用のメソッドを利用できます。クエリー用のメソッドは重ねがけできます。

クエリー用のメソッドはそれを読んだ時点では、まだ検索などの処理は実行されません。実行部分にあたるメソッドを読んで初めて実行されます。
実行部分
実行部分として利用できるメソッドは以下のようなものがあります。

データのソート
実際にタスク管理アプリのタスク一覧を作成日の新しい順に表示できるようにします。
作成日時の新しい順で検索するには、ORDER BY created_at DESCというようなORDER BY節をSQLにつける必要があります。
TasksController(app/controllers/tasks_controller.rb)のindexアクションを以下の通りに変更します。
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
@tasks = current_user.tasks.order(created_at: :desc);
end
・・・
end
scopeの活用
scopeというメソッドを用いることで、クエリー用のメソッドの連続した呼び出し部分をまとめてカスタムのクエリー用のメソッドとして扱うことができます。
Taskモデル(app/models/task.rb)にscopeを定義します。
app/models/task.rb
class Task < ApplicationRecord
before_validation :set_nameless_name
validates :name, presence: true
validates :name, length: { maximum: 30 }
validate :validate_name_not_including_comma
belongs_to :user
scope :recent, -> { order(created_at: :desc)}
private
・・・
end
これでrecentという名前のscopeを追加できました。
以下のようにクエリー用のメソッドの一種として使うことができます。
tasks = Task.recent // 全権を新しい順に取得
task = Task.recent.first // 最も新しいタスクのオブジェクトを取得
task = Task.recent.last // 最も古いタスクのオブジェクトを取得
tasks = current_user.tasks.recent // ログインユーザーのタスクを新しい順で取得
tasks = Task.where(user_id: [1,2,5]).recent // IDが1または2または5のユーザーのタスクを新しい順に取得
フィルタの追加
app/controllers/tasks_controller.rbでは複数のアクションで「idパラメータからタスクオブジェクトを検索して@taskに代入する」という意図の以下のコードが使用されています。
@task = current_user.tasks.find(params[:id])
このように同じコードが重複している場合、この処理に対して変更を行う時に重複箇所全てに変更が必要になります。そこでフィルタを利用して処理の重複を避けるようにします。
app/controllers/tasks_controller.rbを以下のように変更します。
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
def index
@tasks = current_user.tasks.order(created_at: :desc);
end
def show
end
def new
@task = Task.new
end
def create
@task = current_user.tasks.new(task_params)
if @task.save
redirect_to tasks_url, notice: "タスク 「#{@task.name}」を登録しました。"
else
render :new
end
end
def edit
end
def update
task.update!(task_params)
redirect_to tasks_url, notice: "タスク「#{task.name}」を更新しました。"
end
def destroy
task.destroy
redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。"
end
private
def task_params
params.require(:task).permit(:name, :description)
end
def set_task
@task = current_user.tasks.find(params[:id])
end
end
まずはset_taskメソッドをprivateメソッドとして定義して共通化したい処理を記述します。
次にbefore_actionメソッドを利用することで、set_taskメソッドが各アクションの実行前に呼び出されるようになりました。
最後に各アクションから共通化した処理を削除します。
このように共通処理をフィルタとして切り出すことで、アクションをシンプルに記述できます。
URLを自動でリンクとして表示
タスクの詳しい説明にURLを記述したいケースが考えられます。URL部分がリンクになっていてクリックした時遷移できるようにしたいです。
しかし文字列の中からURLに該当する部分を検出して、<a>タグを作成せねばならないため手間がかかります。
rails_autolinkというgemを利用すると、このような処理を自動で行えます。
Gemfileに以下のコードを追加してbundleを実行します。
gem 'rails_autolink'
タスクの詳細画面(app/views/tasks/show.html.slim)の詳しい説明を表示する箇所を以下のように修正します。
h1 タスクの詳細
.nav.justify-content-end
= link_to '一覧', tasks_path, class: 'nav-link'
table.table.table-hover
tbody
tr
th= Task.human_attribute_name(:id)
td= @task.id
tr
th= Task.human_attribute_name(:name)
td= @task.name
tr
th= Task.human_attribute_name(:description)
td= auto_link(simple_format(h(@task.description), {}, sanitize: false, wrapper_tag: "div"))
tr
th= Task.human_attribute_name(:created_at)
td= @task.created_at
tr
th= Task.human_attribute_name(:updated_at)
td= @task.updated_at
= link_to '編集', edit_task_path, class: 'btn btn-primary mr-3'
= link_to '削除', @task, method: :delete, data: { confirm: "タスク「#{@task.name}」を削除します。よろしいですか?" }, class: 'btn btn-danger'
これでタスクの詳しい説明にURLを記述した場合、自動でリンク表示されます。
まとめ
以上、タスク管理アプリケーションに複数の機能を追加し、複雑な現実に合わせて様々な点で強化できました。
次回からはRailsの自動テストについて学習していきます。
コメント