9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Ruby on Rails]検索機能の実装 gem無し 初心者

Last updated at Posted at 2021-07-08

検索機能の実装

検索対象は ユーザーか本の投稿か選べる。 検索方法も、完全一致、前方一致、後方一致、部分一致と選べる。

スクリーンショット 2021-07-08 12.51.19.png

スクリーンショット 2021-07-08 12.52.16.png

コントローラーの作成

rails g controller Searches search

ルーティング記述

get '/search', to: 'searches#search'

検索フォーム作成

ヘッダーに入れたいので、部分テンプレートにします。

application.html.erb
    <% if user_signed_in? %>
          <%= render 'layouts/search' %>
    <% end %>
views/layouts/_seach.html.erb
<div class="serch_form">
    <%= form_with url: search_path,method: :get, local: true do |f| %>
    <%= f.text_field :content %>
    <%= f.select :model,options_for_select({"User"=>"user","Book"=>"book"})%>
    <%= f.select :method,options_for_select({"完全一致"=>"perfect","前方一致"=>"forward",
        "後方一致"=>"backward","部分一致"=>"partial"})%>
    <%= f.submit'検索' %>
    <% end %>
</div>

serch_pathルーティングに検索内容を渡しています。
ユーザーの入力した検索ワードはcontentとしてコントローラーのアクションに送られます。
初めましてな記述はf.select~でしょうか。この記述でプルダウン(セレクトボックス)が作成できます。
({"完全一致"=>"perfect","前方一致"=>"forward"...この部分の記述ですが、
"選択肢で表示する文字"=>DBに渡す値(保存させる内容)となっています。

ちなみに...
<%=f.select:model,options_for_select({"User"=>"user","Book"=>"book"}),include_blank:"選択"%> のように最後にinclude_blank:"選択"を追加するとこんな感じになります。今回の場合だと見にくいですね。

スクリーンショット 2021-07-08 12.48.43.png

:fist:セレクトボックスの作成方法は以下のページで理解できました。

まとめ:検索ワードはcontentで、検索対象はmodelで、検索方法はmethodでコントローラーに渡される。

コントローラー記述

seaches_controller.rb
class SearchesController < ApplicationController
	before_action :authenticate_user!

	def search
	  @model=params[:model]
	  @content=params[:content]
	  @method=params[:method]
	  if @model == 'user'
	    @records=User.search_for(@content,@method)
	  else
	    @records=Book.search_for(@content,@method)
	  end
	end
end

入力、選択された値をparamsで受け取って@~に代入しています。
paramsとは、フォームなどによって送られてきた情報(パラメーター)を取得するメソッドです。
その後に、@modelの値がuserだった場合と、bookだった場合で条件分岐しています。
そして@recordsに入れているのは検索結果です。

モデルファイルに検索条件の分岐を書く。

ちなみにコントローラーの記述を少し変更すれば、
別にモデルファイルに書かなくても、コントローラー内に記載することも可能です。それについては後述します。

じゃあ!なんで!わざわざモデルファイルに書くのか!?
以下の記事でなんとなく...理解できます。 簡単にいうと、あんまりコントローラーに書くと理解しにくい。わかりにくいから。

https://qiita.com/leavescomic1/items/99f32f45cd04035f146c

book.rb
class Book < ApplicationRecord
	# 以下3行は検索機能と関係なしです。 
	belongs_to :user
	validates :title, presence: true
	validates :body, presence: true, length: {maximum: 200}
	
  def self.search_for(content, method)
    if method == 'perfect'
      Book.where(title: content)
    elsif method == 'forward'
      Book.where('title LIKE ?', content+'%')
    elsif method == 'backward'
      Book.where('title LIKE ?', '%'+content)
    else
      Book.where('title LIKE ?', '%'+content+'%')
    end
  end
end

method == 'perfect'は、完全一致です。(viewファイルでそう定義してます。)
forwardは、前方一致。あとはわかりますよね。。

Like句で検索

[前方一致]
モデル名.where('カラム名 like ?','検索したい文字列%')
[後方一致]
モデル名.where('カラム名 like ?','%検索したい文字列')

以下の記事で詳しくわかります。

user.rbにも同様に記載してください。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :books
  attachment :profile_image, destroy: false

  validates :name, length: { minimum: 2, maximum: 20 }, uniqueness: true
  validates :introduction, length: { maximum: 50 }

  def self.search_for(content, method)
    if method == 'perfect'
      User.where(name: content)
    elsif method == 'forward'
      User.where('name LIKE ?', content + '%')
    elsif method == 'backward'
      User.where('name LIKE ?', '%' + content)
    else
      User.where('name LIKE ?', '%' + content + '%')
    end
  end
end

viewページ作成

部分テンプレート使わないとこんな感じ...長くて申し訳ないです。
<div class="container">
  <div class="col-xs-12">
    <% if @model == "user" %>
    <h2>Users search for '<%= @content %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Name</th>
                <th>Introduction</th>
            </tr>
        </thead>
    <% @records.each do |user| %>
        <tbody>
            <tr>
                <th>
                    <%= attachment_image_tag(user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:'40x40') %>
                </th>
                <th>
                    <%= user.name %>
                </th>
                <th>
                    <%= user.introduction %>
                </th>
            </tr>
        </tbody>
    <% end %>
    <% elsif @model == "book" %>
    <h2>Books search for '<%= @content %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Title</th>
                <th>Opinion</th>
            </tr>
        </thead>
        <% @records.each do |book| %>
        <tbody>
            <tr>
                <th>
                    <%= attachment_image_tag(book.user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:'40x40') %>
                </th>
                <th>
                    <%= book.title %>
                </th>
                <th>
                    <%= book.body %>
                </th>
            </tr>
        </tbody>
        <% end %>
    <% end %>
    </table>
  </div>
</div>
![スクリーンショット 2021-07-08 12.51.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/7f975d1b-7fb4-83a6-c657-e98ea9819b2a.png)
部分テンプレートを使う場合。
search.html.erb
<% if @model == 'user' %>
	<h3>Users search for "<%= @content %>"</h3>
  <%= render 'users/index', users: @records %>
<% elsif @model == 'book' %>
	<h3>Books search for "<%= @content %>"</h3>
  <%= render 'books/index', books: @records %>
<% end %>
users/_index.html.erb
<table class='table'>
  <thead>
    <tr>
      <th>image</th>
      <th>name</th>
      <th colspan="3"></th>
    </tr>
  </thead>
  <tbody>
    <% users.each do |user| %>
      <tr>
        <td><%= attachment_image_tag(user, :profile_image, :fill, 50, 50, fallback: "no-image-icon.jpg") %></td>
        <td><%= user.name %></td>
        <td><%= link_to 'Show', user_path(user), class: "user_#{user.id}" %></td>
      </tr>
    <% end %>
  </tbody>
</table>
books/_index.html.erb
<table class='table table-hover table-inverse'>
  <thead>
    <tr>
      <th></th>
      <th>Title</th>
      <th>Opinion</th>
      <th colspan="3"></th>
    </tr>
  </thead>
  <tbody>
    <% books.each do |book| %>
      <tr>
        <td><%= link_to user_path(book.user) do %>
          <%= attachment_image_tag(book.user, :profile_image, :fill, 50, 50, fallback: "no-image-icon.jpg") %>
          <% end %>
        </td>
        <td><%= link_to book.title, book_path(book), class: "book_#{book.id}" %></td>
        <td><%= book.body %></td>
      </tr>
    <% end %>
  </tbody>
</table>

検索条件の分岐をモデルファイルではなくコントローラーに書きたい場合

こちらの記事では、モデルファイルではなくコントローラーに記述しています。

https://qiita.com/IsakiMatsuo/items/69f406d85dbd0ba62625

その他、参考にさせていただいた記事

https://qiita.com/hapiblog2020/items/6c2cef49df5616da9ae3

以上です

記事を書きながら作成していったので、おそらく漏れはないかと思います...。
9
9
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
9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?