[RUBY] Implement follow function in Rails

Why i wrote this article

There are many articles that describe the follow feature in Rails.

However, I felt that there were few articles explaining the implementation procedure up to "List page of users who ** follow me **" and "List page of users who ** follow me **". ..

This "follow list" and "follower list" can be confusing if you don't ** name the columns in the Relationship table carefully.

following, follower, follow, followedVarious naming methods are mixed depending on the article, but since there were few articles that explained the naming method that is the least confusing, I decided to create an article again.

Let me start with the conclusion.

Keep in mind that in this article, ** followers should be `following ** and ** followers should be `follower``` **.

The usual feeling is, "Isn't the follower follower? You may feel. I won't explain it in detail because it will be long, but if you use that naming method, the column to be referred to when displaying the "follower list" of a user is not the `` `follower column, but the other column. Since it becomes a column, various inconveniences occur.

It doesn't matter if you don't understand this meaning at this point, so here ** the followers are `following``` **, ** the followers are `follower``` **. Please be aware that.

Premise

flow

  1. Make a Relationship model
  2. Form an association
  3. Make a routing
  4. Define the action in the controller
  5. Edit the view

Make a Relationship model

In the follow function, both the follower and the follower are users, so it is necessary to create a "many-to-many" relationship between the records in the User table.

To achieve this, we need to create an intermediate table that stores information about who to follow and who to follow.

For example, if User A follows User B, record the information as follows:

** User table **

id name
1 User A
2 User B

** Relationship table **

id following follower
1 2

Type the following into the terminal to actually create the Relationship table, which is an intermediate table.

$ rails g model relationship following_id:integer follower_id:integer
$ rails db:migrate

Now you have an intermediate table called Relationship! The significance of the existence of this table is to save the following two pieces of information.

association

The information `following``` and `follower``` in the Relationship model you just created must both be referenced from the parent User table. (Because both the follower and the follower are Users.)

By creating a following model and a follower model in a pseudo manner, the reference destination can be separated well.

Please change the Relationship model as follows.

app/models/relationship.rb


class Relationship < ApplicationRecord
    belongs_to :following, class_name: "User"
    belongs_to :follower, class_name: 'User'
end

By describing User in class_name, it is indicated that each reference destination is the User model.

Next, let's describe the association on the User model side.

First, add the following code together.

app/models/user.rb


class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  #Insert the following

  has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy

  has_many :following_user, through: :following, source: :follower
  has_many :follower_user, through: :follower, source: :following

  def follow(user_id)
    following.create(follower_id: user_id)
  end

  def unfollow(user_id)
    following.find_by(follower_id: user_id).destroy
  end

  def following?(user_id)
    following_user.include?(user_id)
  end
end

I will explain in order. First of all, about the following parts.

  has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy

In the first has_many, we are working on storing the user_id in the following_id of the Relationship model. At that time, foreign_key specifies the column that you want to explicitly store as `` `folowings_id```.

In the second has_many, we are working on storing the user_id in the follower_id of the Relationship model. At that time, foreign_key specifies `` `follower_id``` and the column you want to explicitly store.

Next, about the following parts.

  has_many :following_user, through: :following, source: :follower
  has_many :follower_user, through: :follower, source: :following

Here, I'm just making an association using through to easily get the users I'm following and the users I'm following.

By doing this, you can get the information of the users you followed by writing, for example, @ user.following_user.

Finally about this part

  def follow(user_id)
    following.create(follower_id: user_id)
  end

  def unfollow(user_id)
    following.find_by(follower_id: user_id).destroy
  end

  def following?(user_id)
    following_user.include?(user_id)
  end
end

Here, we have created a function to follow, a function to unfollow, and a function to check if the user is already following.

This completes the description of the most difficult association with the follow function! Once you get here, it's easy if you understand the basic MVC!

routing

In preparation for defining actions to follow and unfollow, let's first route as follows.

config/routes.rb


  post 'follow/:id' => 'relationships#follow', as: 'follow'
  delete 'unfollow/:id' => 'relationships#unfollow', as: 'unfollow'

Next, in order to create the "Follow List" and "Follower List" pages, perform the following routing as well.

config/routes.rb


  resources :users, only: [:show] do
    #Insert the following
    get :following, :follower, on: :member
  end

controller

Next is the definition of the action in the relationships controller.

First, let's create a relationships controller by typing the following code in the terminal.

$ rails g controller Relationships new

Define a follow action that allows you to follow a user and an unfollow action that allows you to unfollow a user as follows:

app/controllers/relationships_controller.rb


class RelationshipsController < ApplicationController
    def follow
      current_user.follow(params[:id])
      redirect_back(fallback_location: root_path)
    end
  
    def unfollow
      current_user.unfollow(params[:id])
      redirect_back(fallback_location: root_path)
    end
end

Next, describe the actions required to display the "follow list" and "follower list" on the users controller as follows.

app/controllers/users_controller.rb


class UsersController < ApplicationController
    def show
        @user = User.find(params[:id])
    end

    #Insert the following

    def following
        @user  = User.find(params[:id])
    end
  
    def follower
        @user  = User.find(params[:id])
    end
end

All you have to do is write it in the view! !!

View

Here is an example of writing a follow button and an unfollow button on the user # show page.

This is just an example, so please rewrite it as appropriate to suit your product.

html:app/views/users/show.html.erb


<% if user_signed_in? %>
  <% unless @user == current_user %>
    <% if current_user.following?(@user) %>
      <%= link_to unfollow_path(@user.id), method: :delete, :style=>"color:#E0245E;" do %>
        <i class="fas fa-heart"></i>
      <% end %>
    <% else %>
      <%= link_to follow_path(@user.id), method: :post do %>
        <i class="far fa-heart"></i>
      <% end %>
    <% end %>
  <% end %>
<% end %>

Below is an example of a link code to jump to the "Follow List" and "Follower List".

html:app/views/users/show.html.erb


<a href="<%= following_user_path(@user) %>">
  <%= @user.following.count %> following
</a>
<a href="<%= follower_user_path(@user) %>">
  <%= @user.follower.count %> followers
</a>

Also, in the "Follow list" and "Follower list", refer to the following and describe the information you want to display as appropriate.

html:app/views/users/following.html.erb


<% @user.following_user.each do |user| %>
  <% user.name %>
<% end %>

html:app/views/users/follower.html.erb


<% @user.follower_user.each do |user| %>
  <% user.name %>
<% end %>

This is completed! Thank you for your hard work!

Recommended Posts

Implement follow function in Rails
Implement application function in Rails
Rails follow function
Implement simple login function in Rails
Implement CSV download function in Rails
Implement user follow function in Rails (I use Ajax) ②
Implement user follow function in Rails (I use Ajax) ①
[Rails] Implement search function
Implemented follow function in Rails (Ajax communication)
Implement markdown in Rails
Implement star rating function using Raty in Rails6
How to make a follow function in Rails
[Rails] Implement User search function
Implement LTI authentication in Rails
[Rails] (Supplement) Implemented follow function
Implement import process in Rails
[Rails] Implement image posting function
Implement post search function in Rails application (where method)
[Rails] Implement credit card registration / deletion function in PAY.JP
Add a search function in Rails.
Implement tagging function in form object
Implement PHP implode function in Java
Implement a contact form in Rails
Implement login function in Rails simply by name and password (1)
Implement login function in Rails simply by name and password (2)
Implement login function simply with name and password in Rails (3)
Implement user registration function and corporate registration function separately in Rails devise
[Rails] A simple way to implement a self-introduction function in your profile
How to implement search functionality in Rails
[Rails] Function restrictions in devise (login / logout)
I tried to implement Ajax processing of like function in Rails
[Rails 6] Ranking function
[Ruby on Rails] Follow function implementation: Bidirectional
Implement Rails pagination
[Rails] Category function
Group_by in Rails
Implement star evaluation function in Rails 6 (Webpacker is installed as standard)
How to implement ranking functionality in Rails
Implement button transitions using link_to in Rails
[Rails 6] Asynchronous (Ajax) follow function is implemented
[Rails] Notification function
Create authentication function in Rails application using devise
Implement share button in Rails 6 without using Gem
Posting function implemented by asynchronous communication in Rails
[Rails] Implementation of retweet function in SNS application
How to implement a like feature in Rails
Implement the Like feature in Ajax with Rails.
Implement iteration in View by rendering collection [Rails]
Simple notification function in Rails (only when followed)
Model association in Rails
Adding columns in Rails
Follow function (Ajax) implementation
Disable turbolinks in Rails
[Rails] Implemented hashtag function
CSRF measures in Rails
How to implement image posting function using Active Storage in Ruby on Rails
[rails] tag ranking function
Implement Rails account BAN
^, $ in Rails regular expression
Rails search function implementation
Use images in Rails