Rails learning How to implement search function using ActiveModel

Add search function using form_with

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

Review of ActiveRecord

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.

How to use ActiveModel

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

Addition of search function

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

Rails learning How to implement search function using ActiveModel
How to implement search functionality in Rails
How to implement image posting using rails
[Rails] Implement search function
How to implement the breadcrumb function using gretel
How to implement search function with rails (multiple columns are also supported)
Search function using [rails] ransack
[Rails] How to implement scraping
Try to implement tagging function using rails and js
[Rails] How to implement star rating
[Rails] I tried to implement "Like function" using rails and js
[Rails] How to upload images using Carrierwave
[Swift] How to implement the Twitter login function using Firebase UI ①
[Swift] How to implement the countdown function
How to implement TextInputLayout with validation function
[Swift5] How to implement animation using "lottie-ios"
[Swift] How to implement the Twitter login function using Firebase UI ②
[Rails] How to handle data using enum
How to implement a slideshow using slick in Rails (one by one & multiple by one)
[Ruby On Rails] How to search the contents of params using include?
[Rails] Implementation of search function using gem's ransack
[Rails] How to create a graph using lazy_high_charts
[Swift] How to implement the LINE login function
Implement partial match search function without using Ransuck
[swift5] How to implement the Twitter share function
Implement star rating function using Raty in Rails6
[Rails] How to implement unit tests for models
[For beginners] How to implement the delete function
Flow to implement image posting function using ActiveStorage
[Rails] How to upload multiple images using Carrierwave
[Swift] How to implement the fade-in / out function
[Rails] How to easily implement numbers with pull-down
How to make a follow function in Rails
[Rails] How to easily introduce slick (slider function)
How to write Rails
How to uninstall Rails
Rails search function implementation
Rails API mode I tried to implement the keyword multiple search function using arrays and iterative processing.
Continued ・ Flow to implement image posting function using ActiveStorage
How to implement login request processing (Rails / for beginners)
[Rails] How to search by multiple values ​​with LIKE
How to implement guest login in 5 minutes in rails portfolio
Implement post search function in Rails application (where method)
How to implement a like feature in Ajax in Rails
[Rails, JS] How to implement asynchronous display of comments
How to write a date comparison search in Rails
[Rails] How to install a decorator using gem draper
How to implement a circular profile image in Rails using CarrierWave and R Magick
Implement application function in Rails
[rails] How to post images
How to add ActionText function
Implement follow function in Rails
[Rails] How to use enum
[Rails] How to install devise
[Rails] How to use enum
How to read rails routes
[Rails 6] Implementation of search function
How to use rails join
Implement category function using ancestory
How to terminate rails server
How to write Rails validation