[For Rails beginners] Implemented multiple search function without Gem

Preface

Implement the search function with multiple models without using ransack. The search method and search model can be selected from the pull-down menu. The search screen will be implemented directly under the header, and the search result display screen will create a new View. I'm a newcomer about a month after I started studying rails, so please miss any strange expressions or descriptions ... (or point out in the question)

Implementation environment / premise

ruby '2.6.3' gem 'rails', '~> 5.2.4', '>= 5.2.4.3' gem'bootstrap-sass','~> 3.3.6' (not required)

Please create User model and Post model in advance. I have implemented Devise.

Specific content to implement

On the site posted by User, enter the following three inputs in the header, search and transition to the display screen.

① Select User search or post (Book model in my app) search from the pull-down menu (form_with) (2) Select the selection method with the select tag to select exact match, prefix match, suffix match, and partial match. ③ Enter a keyword -Display the search results in View (make it possible to change the display contents of the search results with an if statement)

Image after mounting

Search form ☟

image.png

View of search result display (search result of test / Book / partial match) ☟

image.png

Let's implement it!

① Create a controller

$ rails g controller Search search

** Explanation (click) ** I'm also creating a search action

② Route description

routes.rb


get '/search' => 'search#search'

③ Form creation (using form_with)

** Explanation (click) ** -I make the header part partial, but I think that it can be implemented without problems if you describe the following in application.html.erb, and I think that it can also be implemented by describing it in a specific View. * The class name is a description for reflecting bootstrap. If you have a different version or have not installed Gem, you do not need to describe it. (Please style it yourself)

_header.html.erb


*The code above this line is omitted
<div class="row">
  <div class="col-xs-6 col-xs-offset-3 text-center" style="margin-top: 25px;">
    <% if user_signed_in? %>
      <%= form_with url:search_path, method: :get, local: true do |f| %>
        <%= f.text_field 'search[value]' %>
        <%= f.select 'search[model]', options_for_select({ "User" => "user", "Book" => "book" }) %>
        <%= f.select 'search[how]', options_for_select({ "Perfect matching" => "match", "Prefix match" => "forward", "Backward match" => "backward", "Partial Match" => "partical"  }) %>
        <%= f.submit :"Search" %>
      <% end %>
    <% end %>
  </div>
</div>
** Explanation (click) ** -** User_signed_in? ** in the if statement is Devise's helper method that determines whether the user is Signing in. In other words, here, the search form is displayed only when Signing in. -You can pull down with ** f.select ** and ** options_for_select **. For more information on form_with, I think there are many useful sites, so please check it out. -**'Search [model]', options_for_select () ** is ** {"display name (User)" => "model name (user)"} ** you want to refer to. -**'Search [how]', options_for_select () ** is ** {"display name (exact match)" => "variable name (match)"} **. #### ④ Description to the controller

search_controller.rb


class SearchController < ApplicationController
  
  def search
    @model = params["search"]["model"]          #Select model@Assign to model
    @value = params["search"]["value"]          #Searched string(Here value)To@Assign to value
    @how = params["search"]["how"]          #The selected search method how@Assign to how
    @datas = search_for(@how, @model, @value)      #search_Define an instance variable in the argument of for
  end                          #@datas will contain the final search results
  
  private

  def match(model, value)                     #def search_Processing when how is match in for
    if model == 'user'                        #Processing when model is user
      User.where(name: value)                 #Where to find a name that exactly matches the value
    elsif model == 'book'           
      Book.where(title: value)
    end
  end
  
  def forward(model, value)
    if model == 'User'
      User.where("name LIKE ?", "#{value}%")
    elsif model == 'book'
      Book.where("title LIKE ?", "#{value}%")
    end
  end
  
  def backward(model, value)
    if model == 'user'
      User.where("name LIKE ?", "%#{value}")
    elsif model == 'book'
      Book.where("title LIKE ?", "%#{value}")
    end
  end
  
  def partical(model, value)
    if model == 'user'
      User.where("name LIKE ?", "%#{value}%")
    elsif model == 'book'
      Book.where("title LIKE ?", "%#{value}%")
    end
  end
  
  def search_for(how, model, value)    #The information defined in the search action is included in the argument
    case how                              #The process of searching from the conditional branch of when which is the content of how of the search method
    when 'match'
      match(model, value)                 #In the argument of the search method(model, value)Is defined
    when 'forward'                        #For example, if how is match, proceed to def match processing.
      forward(model, value)
    when 'backward'
      backward(model, value)
    when 'partical'
      partical(model, value)
    end
  end
end
#Prefix match
Model name.where("Column name LIKE?", "value%")
#Backward match
Model name.where("Column name LIKE?", "%value")
#Partial Match
Model name.where("Column name LIKE?", "%value%")

⑤ Edit View page

I will describe it in views / search / search.html.erb

search.html.erb


<div class="container">
  <div class="col-xs-12">
    <% if @model == "user" %>
    <h2>Users search for '<%= @value %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Name</th>
                <th>Introduction</th>
            </tr>
        </thead>
    <% @datas.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 '<%= @value %>'</h2>
    <table class="table">
        <thead>
            <tr>
                <th></th>
                <th>Title</th>
                <th>Opinion</th>
            </tr>
        </thead>
        <% @datas.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>
** Explanation (click) ** It feels like turning the contents of ** @ data ** defined in the controller with an if statement and each model with an each statement. The class name is the same as ③ and it is a description for bootstrap. By writing ** <% = @value%> **, the searched character string is displayed. ##### bonus I took some time to understand the process flow of the controller (although I still don't understand it perfectly) I felt that it would be easier to understand if I made a breakpoint in the controller using a function such as byebug and checked the contents of the variable in the terminal, so please try it. Please forgive me because there is a lot of description in the controller! Please refer to this if you like ☟ [My article about DM function x Ajax] (https://qiita.com/IsakiMatsuo/items/9923d0724c69a2dfa259)

Recommended Posts