[RUBY] Implemented follow function in Rails (Ajax communication)

Overview

Follow functions are often handled in applications that handle users such as SNS. When implementing the follow function in Rails, make a many-to-many association and associate the user through. This time, the follow function was implemented on the user list screen (index) and user details screen (show). Also, in consideration of usability, the follow function can be used in Ajax communication.

Premise

1. Creating a Relationship model

Create a Relationship model for many-to-many associations through users. The Relationship model is created as an intermediate table of the User model and handles the id of the User model.

terminal


rails g model Relationship

create_relationships.rb


class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|
      t.references :follower, foreign_key: { to_table: :users }
      t.references :following, foreign_key: { to_table: :users }

      t.timestamps
    end
    add_index :relationships, [:follower_id, :following_id], unique: true
  end
end

By setting "references: 〇〇", the id of the 〇〇 model (table) can be automatically set to the column name 〇〇_id. Since it will refer to non-existent tables such as follower and following table as it is, set the foreign key "foreign_key: {to_table :: users}" and set it to refer to the User table. Set to save user-wide relevance and set "unique: true" not to save the same combination. After setting, create Relationship table with db: migrate.

2. Edit the model

Then edit the Relationship and User models.

relationship.rb


belongs_to :follower, class_name: "User"
belongs_to :following, class_name: "User"

validates :follower_id, presence: true
validates :following_id, presence: true

Set "belongs_to: follower", but since the follower table does not exist, refer to the User table with "class_name:" User "".

user.rb


has_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship", dependent: :destroy
has_many :followings, through: :following_relationships
has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship", dependent: :destroy
has_many :followers, through: :follower_relationships

Get the id with "has_many: following_relationships", but since "following_relationships_id" does not exist, refer to the relationship table with "class_name:" Relationship "", set the foreign key with "foreign_key: follower_id", and set "follower_id" Set to get. When the User is deleted by "dependent:: destroy", the id of the relationship will also be deleted. Get the user id through following_relationships with "has_many: followings, through:: following_relationships". The reverse (follower) is the same setting.

user.rb


def following?(other_user)
  self.followings.include?(other_user)
end

def follow(other_user)
  self.following_relationships.create(following_id: other_user.id)
end

def unfollow(other_user)
  self.following_relationships.find_by(following_id: other_user.id).destroy
end

Create a method for the follow function. "following?" Checks if the user has been followed. "follow" and "unfollow" are methods to follow and unfollow, respectively.

3. Creating a controller

Create a controller for relationship.

terminal


rails g controller relationships

Set the routing. Since the follower list is entwined with User, set the routing of followings and followers actions in user.

routes.rb


resources :users do
  member do
    get :followings, :followers
  end
end
resources :relationships, only: [:create, :destroy]

Since the follower list is entwined with User, set the routing of followings and followers actions in user. Only create and destroy are set as actions for relationships.

Create a relationships controller.

relationships_controller.rb


class RelationshipsController < ApplicationController
  def create
    @user = User.find(params[:relationship][:following_id])
    current_user.follow(@user)
    respond_to do |format|
      format.html {redirect_back(fallback_location: root_path)}
      format.js
    end
  end

  def destroy
    @user = User.find(params[:relationship][:following_id])
    current_user.unfollow(@user)
    respond_to do |format|
      format.html {redirect_back(fallback_location: root_path)}
      format.js
    end
  end
end

Set the operation when a follow or unfollow operation is received. To process the operation with Ajax communication"respond_to do |format|"Check the format with and specify the rendering destination. format format.If there is a corresponding js request in js, ~ js.Process with erb. In html"redirect_back"Display the previous page with, and if it cannot be displayed, root_Head to path.

Add a follower list method to the users controller.

users.rb


def followings
  @user = User.find(params[:id])
  @users = @user.followings.page(params[:page]).per(10)
  render "show_followings"
end

def followers
  @user = User.find(params[:id])
  @users = @user.followers.page(params[:page]).per(10)
  render "show_followers"
end

Note: We are using pagenations to display the user list.

4. Edit view

Create a link to your followers list. Make each number of users visible. Since it is processed by Ajax communication, enclose the relevant part with -div id = "~"-and set the id.

index.html


<% @users.each do |user| %>
~abridgement~
  <div id="following_count_<%= user.id %>">
    <%= link_to "following(#{user.followings.count})", followings_user_path(user),class:"~" %>
  </div>
  <div id="follower_count_<%= user.id %>">
    <%= link_to "Follower(#{user.followers.count})", followers_user_path(user),class:"~" %>
  </div>
<% end %>

show.html


<div id="following_count">
  <%= link_to "following(#{@user.followings.count})", followings_user_path(@user), class:"~" %>
</div>
<div id="follower_count">
  <%= link_to "Follower(#{@user.followers.count})", followers_user_path(@user), class:"~" %>
</div>

In index, id is assigned to each user, so "-div id="following_count_<%= user.id %>"-" "-div id="follower_count_<%= user.id %>"-" Set the id name as.

Next, create a follow button.

:index.html.erb


<% @users.each do |user| %>
~abridgement~
  <% if current_user && user != current_user %>
    <div id="follow_form_<%= user.id %>">
      <% if current_user.following?(user) %>
        <%= render "unfollow", user: user %>
      <% else %>
        <%= render "follow", user: user %>
      <% end %>
    </div>
  <% end %>
<% end %>

:show.html.erb


<% if current_user && @user != current_user %>
  <div id="follow_form">
    <% if current_user.following?(@user) %>
      <%= render "unfollow", user: @user %>
    <% else %>
      <%= render "follow", user: @user %>
    <% end %>
  </div>
<% end %>

As with the list display, index assigns ids to each user, so set the id name as "-div id =" follow_form_ <% = user.id%> "-". Pass the value of @user to the rendering destination user with "user: @user".

Create follow and follower buttons.

follow.html.erb


<%= form_with(model: current_user.following_relationships.build) do |f| %>
  <%= f.hidden_field :following_id, value: user.id %>
  <%= f.submit "To follow", class: "~" %>
<% end %>

unfollow.html.erb


<%= form_with(model: current_user.following_relationships.find_by(following_id: user.id), method: :delete) do |f| %>
  <%= f.hidden_field :following_id %>
  <%= f.submit "Stop following", class: "~" %>
<% end %>

Submit your request using form_with. form_with is set to use Ajax communication by default. If you use form_for, set the option to "remote: true". Enter the user id in "f.hidden_field: following_id".

5. Create js file

The request sent from the form is processed by the action of the controller and goes to js.erb.

javascript:create.js.erb


$("#follow_form_<%= @user.id %>").html("<%= j(render partial: 'users/unfollow', locals: { user: @user }) %>");
$("#follow_form").html("<%= j(render partial: 'users/unfollow', locals: { user: @user }) %>");

$("#following_count_<%= @user.id %>").html('<%= link_to "following(#{@user.followings.count})", followings_user_path(@user), class:"~" %>');
$("#follower_count_<%= @user.id %>").html('<%= link_to "Follower(#{@user.followers.count})", followers_user_path(@user), class:"~" %>');
$("#following_count").html('<%= link_to "following(#{@user.followings.count})", followings_user_path(@user), class:"~" %>');
$("#follower_count").html('<%= link_to "Follower(#{@user.followers.count})", followers_user_path(@user), class:"~" %>');

javascript:destroy.js.erb


$("#follow_form_<%= @user.id %>").html("<%= j(render partial: 'users/follow', locals: { user: @user }) %>");
$("#follow_form").html("<%= j(render partial: 'users/follow', locals: { user: @user}) %>");

$("#following_count_<%= @user.id %>").html('<%= link_to "following(#{@user.followings.count})", followings_user_path(@user), class:"~" %>');
$("#follower_count_<%= @user.id %>").html('<%= link_to "Follower(#{@user.followers.count})", followers_user_path(@user), class:"~" %>');
$("#following_count").html('<%= link_to "following(#{@user.followings.count})", followings_user_path(@user), class:"~" %>');
$("#follower_count").html('<%= link_to "Follower(#{@user.followers.count})", followers_user_path(@user), class:"~" %>');

You can partially render with Ajax communication with the description "$ (" set id "). html ('I want to render html')". Note: Be careful with the settings, as there were some parts that didn't work due to the difference between "and'. I worked with the above description.

Summary

With the above, the follow function can be implemented by Ajax communication. The follow function in the user details (show) had references and was easy to do, but there was no reference to set the follow function in the list (index) and I had a hard time. It was a good study and experience because I was able to achieve it safely through trial and error.

References

[Ruby on Rails] Let's implement the follow function [Rails] Implementation of asynchronous follow function using Ajax

Thank you very much.

Recommended Posts

Implemented follow function in Rails (Ajax communication)
[Rails 6] Asynchronous (Ajax) follow function is implemented
Implement follow function in Rails
Posting function implemented by asynchronous communication in Rails
[Rails] (Supplement) Implemented follow function
Implement user follow function in Rails (I use Ajax) ②
Implement user follow function in Rails (I use Ajax) ①
Rails follow function
Follow function (Ajax) implementation
[Rails] Implemented hashtag function
How to make a follow function in Rails
[Rails, JavaScript] Implemented like function (synchronous / asynchronous communication)
Implement application function in Rails
[Ruby on Rails] Asynchronous communication of posting function, ajax
Ajax bookmark function using Rails
Add a search function in Rails.
Implemented mail sending function with rails
Implement simple login function in Rails
Implement CSV download function in Rails
I tried to implement Ajax processing of like function in Rails
[Rails] Function restrictions in devise (login / logout)
[Ruby on Rails] Follow function implementation: Bidirectional
Create authentication function in Rails application using devise
[Rails 6] Ranking function
Implement star rating function using Raty in Rails6
[Rails] Implementation of retweet function in SNS application
[Rails] Category function
Group_by in Rails
Implement the Like feature in Ajax with Rails.
Implemented comment function
Simple notification function in Rails (only when followed)
[Rails] Notification function
Implement post search function in Rails application (where method)
How to implement a like feature in Ajax in Rails
[Rails] Implement credit card registration / deletion function in PAY.JP
[For Rails beginners] Implemented multiple search function without Gem
Rails application guest login function implemented (devise not used)
[Ruby on Rails] Post image preview function in refile
I want to define a function in Rails Console
Questions about implementing the Like feature (Ajax) in Rails
Model association in Rails
Comment function (Ajax) implementation
[Rails] Implement search function
Disable turbolinks in Rails
CSRF measures in Rails
^, $ in Rails regular expression
Rails search function implementation
Use images in Rails
Understand migration in rails
Split routes.rb in Rails6
Implement markdown in Rails
[Rails] I implemented the validation error message by asynchronous communication!
Implement login function in Rails simply by name and password (2)
Rails sorting function implementation (displayed in order of number of like)
Implement login function simply with name and password in Rails (3)
Implement user registration function and corporate registration function separately in Rails devise
[Rails] Implemented a pull-down search function for Active Hash data