ruby:admin/article/index.html.slim
li
= form_with model: @search_articles_form, scope: :q, url: admin_articles_path, method: :get, html: { class: 'form-inline' } do |f|
=> f.select :category_id, Category.pluck(:name, :id) , { include_blank: true }, class: 'form-control'
.input-group
= f.search_field :title, placeholder: "title", class: 'form-control'
span.input-group-btn
= f.submit 'Search', class: %w[btn btn-default btn-flat]
This time, we will implement the search function using form_with for search. This time, we will add a search function for authors, tags, and text here.
First, fill in form_with so that you can fill in the author, tag, and text.
ruby:admin/article/index.html.slim
li
= form_with model: @search_articles_form, scope: :q, url: admin_articles_path, method: :get, html: { class: 'form-inline' } do |f|
=> f.select :category_id, Category.pluck(:name, :id) , { include_blank: true }, class: 'form-control'
=> f.select :author_id, Author.pluck(:name, :id) , { include_blank: true }, class: 'form-control'
=> f.select :tag_id, Tag.pluck(:name, :id) , { include_blank: true }, class: 'form-control'
.input-group
= f.search_field :title, placeholder: "title", class: 'form-control'
.input-group
= f.search_field :body, placeholder: "Text", class: 'form-control'
span.input-group-btn
= f.submit 'Search', class: %w[btn btn-default btn-flat]
In form_witn
model: @search_articles_form
In the part of, you can see that the process is to create an instance by putting the following information (category_id, title) in the SearchArticleForm class.
However, SearchArticleForm is a class that does not need to be saved in the DB, so ActiveRecord cannot be used. In other words
$ article.title
Methods such as cannot be used
ActiveRecord is like a Ruby and DB translator in a nutshell. Ruby and DB have different language types, but if you use ActiveRecord, it will play a role of connecting the two. For example
$ article.title
Then ActiveRecord will select the title of the article in the DB.
This time I don't need a DB, that is, I can't use ActiveRecord and I can't use convenient methods such as article.title.
ActiveModel When ActiveRecord cannot be used (processing is not performed using DB), ActiveModel is convenient. By including `` `ActiveModel :: Model``` in a class that does not use DB, you can apply code in the same way as ActiveRecord. In other words, you can use a convenient method like article.title.
class SearchArticleForm
include ActiveModel::Model
end
Now you are ready to use `` `article.title``` etc. (Not yet usable)
Furthermore, from here, decide what to use for the `title``` part of the code like`
article.title. In this case, the category, author, tag, title, body. Because you need 5 search functions
searcharticlefrom```Five attributes of the class "category, author, tag, title, body" are required.
class SearchArticleForm
include ActiveModel::Model
attr_accessor :category_id, :integer
attr_accessor :author_id, :integer
attr_accessor :tag_id, :integer
attr_accessor :title, :string
attr_accessor :body, :string #The body itself is text, but the character type when searching for the body is string
end
Ruby attributes that do not use DB can be created by using attr_accessor But attr_accessor can also be rewritten with ActiveModel
class SearchArticlesForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :category_id, :integer
attribute :author_id, :integer
attribute :tag_id, :integer
attribute :title, :string
attribute :body, :string
end
By entering ActiveModel :: Attributes, you can write in exactly the same format as you enter in ActiveRecord.
with this
$ SearchArticlesForm.category_id
$ SearchArticlesForm.author_id
$ SearchArticlesForm.tag_id
$ SearchArticlesForm.title
$ SearchArticlesForm.body
You will be able to use the method in the form of
Now that you can use ActiveModel, then `` `category_id
author_id
tag_id
title
body``` Create a search method using the above methods for the search function.
class SearchArticlesForm
def search
relation = Article.distinct
relation = relation.by_category(category_id) if category_id.present?#category search function
relation = relation.by_author(author_id) if author_id.present? #author search function
relation = relation.by_tag(tag_id) if tag_id.present? #tag search function
title_words.each do |word|
relation = relation.title_contain(word) #title search function
end
body_words.each do |word|
relation = relation.body_contain(word) #body search function
end
relation
end
private
def title_words
title.present? ? title.split(nil) : []
end
def body_words
body.present? ? body.split('') : []
end
end
Explanation ① distinct
relation = Article.distinct
You can use discount to combine duplicate Articles into one
Explanation ② by_category
relation = relation.by_category(category_id) if category_id.present?
If you fill in without omitting
relation = Article.distinct.by_category(category_id) if category_id.present?
Becomes The range for checking for duplicates with distinct is specified by by_category (category_id) If you look at article.rb where scope is written, you can see that by_category (category_id) is a range specification because it is a range specification.
article.rb
scope :by_category, ->(category_id) { where(category_id: category_id) }
scope :title_contain, ->(word) { where('title LIKE ?', "%#{word}%") }
It has become. The scope of the above category specifies "the range of category_id". The scope of title specifies "the range in which the characters in word are included in title". I have to add the author, tag, and scope of the text, so I will add it.
article.rb
scope :by_category, ->(category_id) { where(category_id: category_id) }
scope :by_author, ->(author_id) { where(author_id: author_id) }
scope :by_tag, ->(tag_id) { joins(:tags).where(article_tags: { tag_id: tag_id }) }
scope :title_contain, ->(word) { where('title LIKE ?', "%#{word}%")
scope :body_contain, ->(word) { joins(:sentences).merge(where('sentences.body LIKE ?', "%#{word}%")) }
Only the way to write by_tag and body_contain is different. It is due to the attributes (methods) that Article is confident about. Article has category, author, and title attributes, but not tag and body attributes. In other words, Article.tag and Article.body cannot be created. Since there is a many-to-many relationship between Article and tag, the body is via sentence via article_tag. Therefore, for those two, joins are used to scope.
Explanation ③ title_words
title_words.each do |word|
relation = relation.title_contain(word) #title search function
end
private
def title_words
title.present? ? title.split(nil) : []
end
The statements below title_words are ternary operators and are replaced in this way
def title_words
if title.present?
title.split(nil)
else
[]
end
end
Words with half-width spaces can be split and examined with split (nil).
Recommended Posts