** This time I would like to do a category search using active hash **
I'm making an app that allows me to share my impressions of coffee. It's like a coffee version of Instagram.
In this way, select a category and post like "Region Latin America, Rich"
The selected category is displayed firmly.
Try searching with the name Guatemala and the region Latin America.
The search results are displayed firmly! !!
This time, we will introduce a gem called ** active hash ** for category selection and a gem called ** ransack ** that can easily implement the search function.
Active hash means that there is "basically unchanged data" such as a list of prefecture names and categories. There is no need to store it in the database as it is basically unchanged data. On the other hand, if you write those data directly in a view file etc., it will not be readable. Active Hash is useful in such cases. It is a Gem that describes data that does not change such as prefecture name in the model and can handle it as if it were stored in the database. That is, you can use ActiveRecord methods for data such as prefecture names. You don't have to wastefully increase the number of tables.
ransack is a gem that allows you to create simple and advanced search forms.
Let's introduce these gems first.
We have set up various categories, but this time we will focus only on the acidity category, which expresses sourness.
acidity.rb
class Acidity < ActiveHash::Base
self.data = [
{ id: 2, name: 'LOW(Few)' },
{ id: 3, name: 'MEDIUM(Moderate)' },
{ id: 4, name: 'HIGH(strong)' }
]
end
This is the category created by active hash.
Save the coffee post, save the acidity_id in the drinks table
drinks.rb
class Drink < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :user
has_one :trade
has_many :drink_tag_relations
has_many :tags,through: :drink_tag_relations
has_one_attached :image
belongs_to_active_hash :region
belongs_to_active_hash :body
belongs_to_active_hash :acidity
belongs_to_active_hash :processing
with_options presence: true do
validates :name
validates :explain
end
end
belongs_to_active_hash :acidity
By writing, acidity and association will be formed, and you will be able to select a category.
extend ActiveHash::Associations::ActiveRecordExtensions
By writing and incorporating the module, belongs_to_active_hash method Can be used
drinks/new.html.erb
<%= f.collection_select(:acidity_id,Acidity.all,:id,:name,{},{class: "You can set the class like this"})%>
You can implement the category like this
The first argument is the column name of the save destination, this time acidity_id
Specify the array data you want to display in the second argument, Acidity.all
DB column name to be referenced when displaying in the third argument
Column name actually displayed in the 4th argument
Strictly speaking, the name in Acidity.rb is not a column, Since it can be treated like a database, specify name
You have now created a pull-down category selection field like the one above.
routes.rb
Rails.application.routes.draw do
root to: 'drinks#index'
get '/drinks/searchdrink', to: 'drinks#search_drink'
resources :drinks, only: [:index,:new,:show,:create,:destroy] do
collection do
get 'search'
end
end
** If you do not write it on resources, you may be transferred to an unintended screen, so write it above it! ** **
drinks_controller.rb
class DrinksController < ApplicationController
include SessionsHelper
before_action :create_searching_object,only: [:index,:search_drink]
def index
@user = current_user
@drinks = Drink.all.order("created_at DESC")
end
def new
@drink = DrinkTag.new
end
def create
@drink = DrinkTag.new(drink_params)
if @drink.valid?
@drink.save
redirect_to drinks_path
else
render 'new'
end
end
def search_drink
@results = @p.result
end
private
def drink_params
params.require(:drink_tag).permit(:name,:price,:explain,:image,:tag_name,:region_id,:body_id,:acidity_id,:processing_id).merge(user_id: current_user.id)
end
def create_searching_object
@p = Drink.ransack(params[:q])
end
The index action gets the information of all posts
The create_searching_object action uses the key (: q) to search the drinks table for product information. I'm creating a search object named @p
Since it is used only in the index and search_drink actions, it is limited by before_action.
In the search_drink action, for @p, by setting it to .result, the search result is acquired and assigned to @result.
That's all for the controller.
Here, let's implement a post search form. At that time, we use two methods, "search_form_for" and "collection_select".
search_form_for is a helper method that generates a ransack-specific search form.
The collection_select method is a helper method that can display the information in the DB in a pull-down format.
drinks/index.html.erb
<%= search_form_for @p, url: drinks_searchdrink_path do |f| %>
<%= f.search_field :name_cont%>
<%# _cont is used when it is a string instead of id%>
<p>Category search</p>
<%#The base is a drink class, with the second argument%>
<%= f.label 'acidity'%>
<%= f.collection_select :acidity_id_eq,Acidity.all,:id,:name,include_blank: 'unspecified' %>
<%= f.submit 'Search' %>
<% end %>
A search form is generated by passing "@p (search object)" as an argument of search_form_for.
I want to skip the url to drink # search_drink, so I checked it with rails route and it became like this
<%= f.collection_select :acidity_id_eq,Acidity.all,:id,:name,include_blank: 'unspecified' %>
First argument: Column name you want to search
Second argument Specify the array data you actually want to display
In this case, it's Acidity.all.
Third argument DB column name to be referenced when displaying
Fourth argument The column name that is actually displayed
Option include_black Contents displayed when nothing is selected, this time "Not specified"
After processing the search_drink action, rails will redirect you to search_drink.html.erb by default, so Create search_drink.html.erb
<h1>
search results
</h1>
<%#Conditional branching based on the number of search results%>
<% if @results.length !=0 %>
<% @results.each do |drink| %>
<div class='main'>
<%#Product list%>
<div class='item-contents'>
<h2 class='title'></h2>
<ul class='item-lists'>
<%#If there is something in the instance variable of the product, let's make it possible to expand all the contents%>
<%if drink%>
<li class='list'>
<%= link_to drink_path(drink.id) do %>
<div class='item-img-content'>
<%= image_tag drink.image , class: "item-img" if drink.image.attached? %>
<%# if drink.trade%>
<%# end %>
</div>
<div class='item-info'>
<h3 class='item-name'>
<%= drink.name %>
</h3>
<div class='item-price'>
<span><%= drink.price %>Circle<br>(tax included)</span>
<div class='star-btn'>
<%# image_tag "star.png ", class:"star-icon" %>
<span class='star-count'>0</span>
</div>
</div>
<div class='item-explain'>
<%= drink.explain%>
</div>
<div>
<% if drink.region %>
Origin<%= drink.region.name%>
<% end %>
</div>
<div>
<% if drink.body%>
Rich<%= drink.body.name %>
<% end %>
</div>
<div>
<% if drink.acidity %>
acidity<%= drink.acidity.name%>
<% end %>
</div>
<div>
<% if drink.processing%>
Processing method<%= drink.processing.name%>
<% end %>
</div>
</div>
<% if logged_in? && current_user.id == drink.user_id %>
<div class="item-delete">
<%= link_to "delete",drink_path(drink),method: :delete %>
</div>
<% if drink.trade%>
<%= link_to "Buy goods", drink_trades_path(drink) %>
<% end %>
<% end %>
</li>
<%end%>
</ul>
</div>
<%end%>
</div>
<% end %>
<% else %>
There is no applicable product
<% end %>
<br>
<%= link_to 'Go back to the top page', root_path %>
And the search result is in @result, In each statement, I set the local variable to drink and display as many search results as possible.
This completes the implementation of category search with ransack using the category created with Active hash!
Recommended Posts