はじめに
今回は、投稿データ(Post)を検索バーでキーワード検索を行い、表示させる。また、複数のテーブルを結合させて、その範囲で検索できるように実装していきます。
完成イメージ
※後日掲載予定
環境
MacOS 10.15.7
ruby 2.6.5
Ruby on Rails 6.0.0
執筆者の状況
利用者を「User」、投稿を「Post」、画像を「Image」、都道府県を「Prefecture」(seedデータ)としてテーブルを作成しております。
(テーブルの中身は投稿用に簡略化してます)
リレーションは次の通りです。
has_many :posts, dependent: :destory
belongs_to :user
belongs_to :prefecture
has_many :images, dependent: :destory
accepts_nested_attributes_for :images, allow_destroy: true
belongs_to :post
mount_uploader :image, ImageUploader
has_many :posts
このような形になっています。userとimageも紹介してはいるのですが、今回は関わりが無いので説明を省略します。
prefectureはpost(投稿)を複数持っているので、「has_many :posts」としています。
postはprefectureに属しているので、「belongs_to :prefecture」になります。
作業していきます!
###①検索バーの設定〜機能実装
####①-1 検索バーの作成
このようにして検索バーを作成します。
<%= form_with(url: search_posts_path, local: true, method: :get, class: "rootpageSearchForm") do |form| %>
<%= form.text_field :keyword, placeholder: "キーワードを入力", id: 'Search_Form', class: "rootpageSearchForm__content" %>
<%= form.submit "検索", class: "rootpageSearchForm__bottum" %>
<% end %>
.rootpageSearchForm {
width: auto;
&__content {
width: 40vw;
height: 30px;
color: #000000;
border-radius: 5px;
border-color: #008bbb;
}
&__bottum {
margin-left: 0.5vw;
height: 30px;
width: 150px;
border-radius: 20px;
background-color: #87cefa;
border: none;
box-shadow: 0 0 8px gray;
color: #ffffff;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
}
.rootpageSearchForm__bottum:hover {
background-color: #00bfff;
}
}
####①-2 searchアクションの作成
routes.rbにsearchアクションを新規で定義します。今回はpostアクションにネストさせた形で定義します。
以下のように定義します。
Rails.application.routes.draw do
resources :posts do
collection do
get 'search'
end
end
下記一部省略...
end
今回、検索のキーワードで該当する投稿を一覧として表示させるので、idが不要なため
collection do
get 'search'
end
と定義します。
仮にidを必要とする場合(投稿の詳細ページなど)はcollectionではなく、memberを用いて定義します。
collectionとmemberの違いはURLにidが付くか、付かないかの違いです。
collection ・・・ ルーティングにidが付かない。
member ・・・ ルーティングにidが付く。
###①-3 searchメソッドをpostモデルに定義
class Post < ApplicationRecord
...上記一部省略
def self.search(search)
if search != ""
Post.where('content LIKE(?) OR title LIKE(?), "%#{search}%", "%#{search}%")
else
Post.all
end
end
下記一部省略...
end
whereメソッド
を使って、引数に記述した条件を基に、テーブル内の「条件に一致したレコードのインスタンス」を配列の形で取得。
LIKE句
を使い、曖昧な文字列を検索するようにしています。
文字列 | 意味 |
---|---|
% | 任意の文字列(空白文字列含む) |
_ | 任意の1文字 |
実行例
実行例 | 意味 |
---|---|
where('title LIKE(?)', "a%") | aから始まるタイトル |
where('title LIKE(?)', "%b") | bで終わるタイトル |
where('title LIKE(?)', "d_") | cが含まれるタイトル |
where('title LIKE(?)', "%b") | dで始まる2文字のタイトル |
where('title LIKE(?)', "_e") | eで終わる2文字のタイトル |
引数searchは、検索フォームから送信されたパラメーターが入る。
そのため、if search != ""
と記述し、検索フォームに何か値が入力されていた場合を条件とする。
検索フォームに何も入力されない(空の状態)で検索ボタンが押されると、引数に渡されるsearchの中身が空になるので、その場合の処理をelse文内で記述。
今回はPost.all
として、全ての投稿を取得して表示させます。
テーブルとのやりとりに関するメソッドはモデルに置くのが基本!
####①-4 コントローラーを記述
posts_controller.rbにsearchアクションを定義します。
class PostsController < ApplicationController
...上記一部省略...
def search
@posts = Post.search(params[:keyword]).order(created_at: :desc)
end
...下記一部省略...
searchメソッドの引数にparams[:keyword]
と記述して、検索結果を渡します。
また、検索結果を投稿が新しい順で並べるため、order(created_at: desc)
と記述してます。
####①-5 search.html.erbを作って、検索結果を表示させる
あとは、検索結果画面を表示させるsearch.html.erbを作って完了です。
<div class="wrapper">
<% @posts.each do |post| %>
<div class='contents row'>
...以下省略...
<% end %>
</div>
####①-6 完成!
これで、検索バー並びにキーワード検索により、検索結果を表示できるようになりました。
ここまでの設定では、Postテーブルの「content(投稿文)」と「title(投稿タイトル)」が検索対象になっている状態です。
###② 他のテーブルを結合させて、検索対象に追加する。
今回、都道府県名でも検索できるように、検索対象にPrefectureテーブルの「name(都道府県名)」を追加していきたいと思います。
postモデルに記述を追加していきます。
class Post < ApplicationRecord
...上記一部省略
def self.search(search)
if search != ""
Post.joins(:prefecture).where('content LIKE(?) OR title LIKE(?) OR prefectures.name LIKE(?)', "%#{search}%", "%#{search}%", "%#{search}%")
else
Post.all
end
end
下記一部省略...
end
prefectureテーブルも検索対象にするために、joins(:prefecture)
として追加しています。
joinメソッド
は複数のテーブルを1つに結合したいときに使うメソッド
です。
今回の場合は、postテーブルとprefectureテーブルを結合させて、1つのテーブルとして扱います。
whereメソッドの引数の部分には、OR prefectures.name LIKE(?)'
を追加して都道府県名を検索対象に指定しています。この分の"%#{search}%"
も1つ追加して完了です。
#最後に
初学者のため、間違い等ありましたらご指摘いただけますと幸いです。
最後までご覧いただきありがとうございました。