LoginSignup
2
1

More than 3 years have passed since last update.

【Rails】検索機能

Last updated at Posted at 2020-10-17

検索機能の作り方(覚書)

検索機能はこれからも使うと思うので、覚書です。

(1)ビューを作る

app/views/tweets/ index.html.erb
#検索フォーム

<%= form_with(url: search_tweets_path, local: true, method: :get, class: "search-form") do |form| %>
  <%= form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" %>
  <%= form.submit "検索", class: "search-btn" %>
<% end %>

#検索結果後の画面 (4)のコントローラーで定義したキーワード入り@tweetを引っ張ってきています

<div class="contents row">
  <% @tweets.each do |tweet| %>
    <%= render partial: "tweet", locals: { tweet: tweet } %>
  <% end %>
</div>

(2)searchアクションのルーティングを設定する

collectionとmemberについて

両者の違いは、

  • 「member」は、「resources以外のメソッドを追加したい」且つ「id情報を伴うURIを生成したい」時に使用。
  • 「collection」は、「resources以外のメソッドを追加したい」且つ「id情報を伴わないURIを生成したい」時に使用。
アクション rails routesで見たときの違い 使用例 指定可能なHTTP動詞
member :idが付く 購入機能
(どの商品が選択され購入されたのか特定が必要なのでid指定の必要あり)
getpatchputpostdelete
collection :idが付かない 検索機能
(検索するだけのアクションなのでid指定する必要なし)
GETリクエスト+/photos/searchなどの (idを伴わない) パスを認識

今回はコレクションで。

config/ routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: 'tweets#index'
  resources :tweets do
    resources :comments, only: :create
    collection do
      get 'search'
    end
  end
  resources :users, only: :show
end

(3)モデルに検索処理を行うメソッドを定義

app/models tweet.rb
class Tweet < ApplicationRecord
  validates :text, presence: true
  belongs_to :user
  has_many :comments

  def self.search(search)
    if search != ""
      Tweet.where('text LIKE(?)', "%#{search}%")
    else
      Tweet.all
    end
  end
end
  • 今回はsearchメソッドとする。

  • if search != ""とすることで、検索フォームに何か値が入力されていた場合を条件としている。

  • 検索したキーワードが含まれている投稿を取得するために、whereメソッドとLIKE句を利用する。

whereメソッド

  • モデルが使用できるActiveRecordメソッドの一つ。

  • モデル.where(条件)のように、引数部分に条件を指定することで、テーブル内の「条件に一致したレコードのインスタンス」を配列の形で取得できる。

  • 条件部分には「検索対象となるカラム」を必ず含める。

 whereメソッド
モデル名.where('検索対象となるカラムを含む条件式')
  • whereメソッドを連続して記述することによって複数の条件に一致したレコードを取得することもできる。
 例
Tweet.where('id < 3').where(user_id: 1)

LIKE句

  • 曖昧な文字列を検索する時に使用(whereメソッドとセットで使う)

曖昧文字列について

文字列 意味
% 任意の文字列(空白文字列含む)
_ 任意の一文字

実行サンプル

文字列 意味
where('title' LIKE(?), "a%") aから始まるタイトル
where('title' LIKE(?), "%a") aで終わるタイトル
where('title' LIKE(?), "%a%") aが含まれるタイトル
where('title' LIKE(?), "a_") aで始まる2文字のタイトル
where('title' LIKE(?), "_a") aで終わる2文字のタイトル
  • なお、モデルが肥大化してしまうのを防ぐためにサービスクラスを設定して記載することも可能。

(4)コントローラーに定義

app/controllers/ tweets_controller.rb
class TweetsController < ApplicationController
  before_action :set_tweet, only: [:edit, :show]
  before_action :move_to_index, except: [:index, :show, :search]

  def index
    @tweets = Tweet.includes(:user).order("created_at DESC")
  end

  (省略)

  def search
    @tweets = Tweet.search(params[:keyword])
  end

  private
  def tweet_params
    params.require(:tweet).permit(:image, :text).merge(user_id: current_user.id)
  end

  def set_tweet
    @tweet = Tweet.find(params[:id])
  end

  def move_to_index
    unless user_signed_in?
      redirect_to action: :index
    end
  end
end

  • searchメソッドの引数に、params[:keyword]と記述して、検索結果を渡している。

  • 未ログイン時に検索するとトップページへリダイレクトしてしまうため、before_actionexceptオプション:searchを追加している。

  • (補足)exceptオプションとは、before_actionで使用できるオプション。「除外」するという意味。 つまり、exceptで指定したアクションには、事前処理(move_to_indexメソッド内容)は実行されない。

以上です。

番外編(モデルではなく、検索機能を「サービスクラス」に持たせる場合には)

(1)appディレクトリ配下に、servicesというディレクトリを作成。

(2)その配下に、ファイルを作成。(今回はsearch_tweets_service.rb

app/services/ search_tweets_service.rb
class SearchTweetsService
  def self.search(search)
    if search != ""
      Tweet.where('text LIKE(?)', "%#{search}%")
    else
      Tweet.all
    end
  end
end

(3)呼び出すコントローラーのクラス名を変更する。

  • (2)のファイル名と同じにすること。
app/controllers/ tweets_controller.rb
class TweetsController < ApplicationController

 (省略)

  def search
    @tweets = SearchTweetsService.search(params[:keyword])
  end

 (省略)

end

(4)モデルの記述は削除する。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1