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.
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.
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.
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.
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".
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.
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.
[Ruby on Rails] Let's implement the follow function [Rails] Implementation of asynchronous follow function using Ajax
Thank you very much.
Recommended Posts