[RUBY] [Rails] Sort the post list by date or number of likes using the pull-down box

At the beginning

I wanted a function to sort posts by date and time and the number of likes in the original application, so I managed to implement it while being quite addicted, so I will leave it as a memorandum.

Premise

__ ● Environment __ ・ Ruby 2.6 series ・ Rails 5.2 series __ ● Library used __ ・ Slim ・ Devise __ ● Like function is implemented __ ・ [Rails] Implementation of Like Function (Slim and devise are also introduced in this article)

↓ It looks like this after mounting ↓

(I'm sorry it doesn't look good ...) ezgif.com-video-to-gif.gif

Implementation

1. First, create a pull-down form

h1 post list   = form_with model: @post, url: search_path, method: :get, local: true do |form| = form.select :keyword, [ ['Posts are newest', 'new'], ['Posts oldest', 'old'], ['In descending order of likes', 'likes'], ['In ascending order of likes', 'dislikes'], ] = form.submit   #================The part that was originally implemented========================



 >> __ ・ Let's break down what is happening. __

>>```ruby
= form_with model: @post, url: search_path, 
            method: :get, local: true do |form|

__-The `Post model` is specified as the model used in the form. This will send the data with the value params [: post]. __

__ ・ After searching, we will prepare a page for displaying search results, so set the path to `` `search_path``` and send it as a GET request. __

__ ・ local: true. If this is not specified in form_with, it will be considered as Ajax communication. If you do not use JS, you must add it or it will not be rendered. __

= form.select :keyword, [ ['Posts are newest', 'new'], ['Posts oldest', 'old'], ['In descending order of likes', 'likes'], ['In ascending order of likes', 'dislikes'], ] = form.submit



 >> __ ・ You can create a pull-down form by using the select method.
 The usage is like `` `select (" object "," method "," choice ")` ``. __

 >> __ ・ ``` The first argument object``` corresponds to the `` `post object` ``, but omit it when using form_with. __

 >> __ ・ ``` The second argument method``` is a` `thing that is sent as a parameter to the` `controller, so please describe it freely. __
 __ In this case, since keyword is specified, it will be sent as `` `params [: keyword]` ``. __

 >> __ ・ In the choice``` part, which is the third argument,
 ```[ [Value to be displayed by pull-down 1,Parameters sent to the action], 
 [Value to be displayed in the pull-down part 2,Parameters sent to the action], 
 [Value to be displayed in pull-down, Part 1, Parameter sent to action]] Specify `` `. __

### 2. Edit the routing

>```ruby:config/routes.rb
Rails.application.routes.draw do
#================The part that was originally implemented========================
  devise_for :users
  resources :users
 
  root to: 'posts#index'
  resources :posts, only: [:index, :new, :create, :show, :edit, :update] do
    resource :favorites, only: [:create, :destroy]
  end
#================The part that was originally implemented========================
 
  #Send search results to the search action of the posts controller
+ get 'search' => 'posts#search'
 
end

3. Edit the posts controller

class PostsController < ApplicationController . . . def search selection = params[:keyword] @posts = Post.sort(selection) end . . . end


 >> __ ・ Create a search action. In params [: keyword], the value selected in the sort form is received and stored in a local variable called selection. __
 
 >> __ ・ `sort` is an instance method for Post. After that, define the Post model class so that posts will be retrieved in the order of sorting by the value of selection. __

### 4. Define the sort instance method in the Post model.

>```ruby:app/models/user.rb
class Post < ApplicationRecord
  .
  .
  .
  def self.sort(selection)
    case selection
    when 'new'
      return all.order(created_at: :DESC)
    when 'old'
      return all.order(created_at: :ASC)
    when 'likes'
      return find(Favorite.group(:post_id).order(Arel.sql('count(post_id) desc')).pluck(:post_id))
    when 'dislikes'
      return find(Favorite.group(:post_id).order(Arel.sql('count(post_id) asc')).pluck(:post_id))
    end
  end
  .
  .
  .
end

__ ・ Since it is an instance method definition, self is added. __

__ ・ The case statement defines the conditions for sorting according to the value passed to selection. __

__ ↓ Sort by date __

when 'new' return all.order(created_at: :DESC) when 'old' return all.order(created_at: :ASC)


 >> __ ↓ Sort by number of likes __

>>```ruby
when 'likes'
   return find(Favorite.group(:post_id).order(Arel.sql('count(post_id) desc')).pluck(:post_id))
when 'dislikes'
   return find(Favorite.group(:post_id).order(Arel.sql('count(post_id) asc')).pluck(:post_id))

__ · Favorite.group (: post_id) will group posts with the same post_id. __

__ · order (Arel.sql ('count (post_id) desc')), count how many user_ids are stored in grouped posts and sort from the most (that is, how much I'm counting if it's liked). __ __ʻArel.sql () is a SQL injection countermeasure`. __

__ ・ In pluck (: post_id), post_id itself is acquired. If you do not describe this, the find corresponding to the call to Post will not be found and an error will occur. __

_ * Currently, the problem that posts with 0 likes are not displayed is not supported. I'm sorry…. I will add it as soon as it is resolved. _

5. Display the view of search results

h1 search results   = form_with url: search_path, method: :get, local: true do |form| = form.select :keyword, [ ['Posts are newest', 'new'], ['Posts oldest', 'old'], ['In descending order of likes', 'likes'], ['In ascending order of likes', 'dislikes'], ] = form.submit


 >> __ ・ Create a setatch.html.slim file in the app / views / posts directory and describe it as above. __
 
 >> __ ・ The content described here is exactly the same as the content described in posts / index.html.slim, so it will be partialized. __

### 6. Partial view

 > #### __6-1. Partialization of search form __

>```ruby:app/views/posts/_select_form.html.slim
= form_with model: @post, url: search_path, method: :get, local: true do |form|
  = form.select :keyword, [ ['Posts are newest', 'new'],
                            ['Posts oldest', 'old'],
                            ['In descending order of likes', 'likes'],
                            ['In ascending order of likes', 'dislikes'],
                          ]
  = form.submit

__ · Create a _select_form.html.slim file in the app / views / posts directory and port the entire pull-down menu code. __

__6-2. Partialization of post list & search result list __


 >> __ ・ Create a `_result.html.slim` file in the app / views / posts directory and port the entire code for displaying the post list. __

 > #### __6-3. Render to each view __

>```ruby:app/views/posts/index.html.slim
h1 post list
 
= render 'select_form'
 
= render 'result'

h1 search results   = render 'select_form'   = render 'result'


#### that's all.


## Finally
 This completes the sort function implementation!
 To be honest, I'm not confident in many parts, so I'd be very happy if you could tell me anything wrong!

## Article that I was very helpful
 [How to implement search function without gem? Supports multiple tables! ](Https://prokyou.com/rails/nogemsearch/)
 [[Rails] How to use select to create a select box with a complete understanding form](https://310nae.com/rails-selectbox/)
[select (ActionView::Helpers::FormOptionsHelper) - APIdock](https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select)


Recommended Posts

[Rails] Sort the post list by date or number of likes using the pull-down box
[Enum] Let's improve the readability of data by using rails enum
[Rails] Register by attribute of the same model using Devise
When using the constructor of Java's Date class, the date advances by 1900.
[Rails] How to display the list of posts by category
Pagination sorted by number of likes
How to sort the List of SelectItem
[Rails] Implementation of PV number ranking using impressionist
A review of the code used by rails beginners
[Java] Sort the list using streams and lambda expressions
Find the remainder divided by 3 without using a number
Rails6: Input the initial data of ActionText using seed
Output of the day of the week using Ruby's Date class
Limit the number of threads using Java's Executor Service
Try using the query attribute of Ruby on Rails