[Rails] Asynchronous implementation of like function

1.First of all

As shown in the demo video below, we will implement a function that allows you to like the content posted by the user. Image from Gyazo

2. Prerequisites

Assuming that the user registration function and posting function have already been implemented, add the "Like function" to it. I will proceed with the flow.

I think it will be easier to understand if you can imagine the database structure as shown below. ER 図(Qiita).png

3. Implementation of like function

** ■ Flow until implementation **

Roughly speaking, we will implement it according to the following flow. ・ Creating a model    ↓ ・ Addition of routing    ↓ ・ Creating a controller    ↓ -Create a view

Well then, let's say it's fast.

3-1. Creating a Like model

First, create a Like model. Execute the following command in the terminal.

Terminal


$ rails g model Like

A new migration file will be created, so edit it as follows.

db>migrate>xxxxxx_create_likes.rb


class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      # ===Postscript part===
      t.references :tweet, foreign_key: true, null: false
      t.references :user, foreign_key: true, null: false
      # ===Postscript part===
      t.timestamps
    end
  end
end

If you save as references type as above, you can specify `` `tweet_idanduser_id``` as foreign keys. Now let's run the migration file and create a likes table.

Terminal


$ rails db:migrate

After running the above command, check if the likes table has been created. After confirming that it has been created successfully, the next step is to set up the association.

3-2. Association settings

An association is an association between two models. We will set the associations for the User model and Like model, and the Tweet model and Like model.

User model and Like model association settings

First, set up the association between the User model and the Like model.

The relationship between the two models is as follows. ・ Users can like multiple likes ・ There is only one user who likes A

In other words, the User model and the Like model have a "one-to-many" relationship. Now let's actually write the code.

Please add the code as follows to the User model.

app>models>user.rb


class User < ApplicationRecord

  has_many :tweets, dependent: :destroy

  #Add this line
  has_many :likes

end

has_manyIndicates that there is a "one-to-many" relationship with other models.

Next, add the code below to the Like model.

app>models>like.rb


class Like < ApplicationRecord

  #Add this line
  belongs_to :user

end

belongs_toIshas_manyThe opposite of, it shows that there is a "many-to-one" relationship with other models.

You have now set up the association between the User model and the Like model.

Tweet model and Like model association settings

In the same way, we will set up the association between the Tweet model and the Like model.

The relationship between the two models is as follows. ・ Multiple likes for one post ・ There is only one post linked to Like A

In other words, the Tweet model and the Like model also have a "one-to-many" relationship. Now let's actually write the code.

Please add the code as follows to the Tweet model.

app>models>tweet.rb


class Tweet < ApplicationRecord
  belongs_to :user

  #Add this line
  has_many :likes, dependent: :destroy

end

dependent: :By adding destroy, when a post is deleted, the likes associated with that post will also be deleted.



 Next is the Like model.


#### **`app>models>like.rb`**
```ruby

class Like < ApplicationRecord

  belongs_to :user

  #Add this line
  belongs_to :tweet

end

This completes the association settings for all models.

3-3. Validation settings

Since we want one user to like Post A only once, set the validation so that it cannot be liked more than once.

Please add the following to the Like model.

app>models>like.rb


class Like < ApplicationRecord

  belongs_to :user
  belongs_to :tweet

  #Add this line
  validates :user_id, uniqueness: { scope: :tweet_id }

end

By writing as above, you can prevent user_id and `` `tweet_id``` from overlapping. This completes the validation settings.

3-4. Addition of routing

We will finally implement the like function in earnest.

First, let's add the routing used for the like function. Please add the code as follows.

config>routes.rb


Rails.application.routes.draw do
  devise_for :users,
    controllers: { registrations: 'registrations' }

  resources :tweets, only: [:index, :new, :create, :show, :destroy] do

    #Add this line
    resources :likes, only: [:create, :destroy]

  end

end

We need to add a route to save and delete likes, so we have defined the `create``` and `destroy``` actions for the likes controller.

By nesting the routing, you can specify which post the like is associated with.

After adding the code, don't forget to use the rails routes command to make sure that the routing settings are correct.

3-5. Creating a likes controller

Next, we will create a likes controller. Execute the following command in the terminal.

Terminal


$ rails g controller likes

You can create a likes controller by running the above command.

Now let's create a `create``` action and a `destroy``` action in the created likes controller. Please add the code as follows.

app>controllers>likes_controller.rb


class LikesController < ApplicationController

  # ===Postscript part===
  def create
    @like = current_user.likes.build(like_params)
    @tweet = @like.tweet
    if @like.save
      respond_to :js
    end
  end

  def destroy
    @like = Like.find_by(id: params[:id])
    @tweet = @like.tweet
    if @like.destroy
      respond_to :js
    end
  end

  private
    def like_params
      params.permit(:tweet_id)
    end
  # ===Postscript part===

end

Assuming you understand the private methods and params, let's take a brief look at the added code.

create action

First, `@ like``` contains information about the ```user_id``` of the user who "liked" the post and the `tweet_id``` of the "liked" post. I will. This code uses the build method to create an instance.

Next, @ tweet contains the information of the post associated with `` `@ like, that is, the information of the "liked" post. @tweet```To which post"How nice"Used in creating views to determine if you pressed.

The last if @ like.save part switches the format of the response returned when "Like" is pressed with the `` `respond_to``` method. In order to reflect the view in real time when "Like" is pressed, the response is returned in JS format.

destroy action

There are many parts that overlap with the contents explained in the create action, so to explain briefly, id is determined from the received HTTP request, and the record specified in `` `@ like``` is specified. Contains information.

Again, in order to reflect the view in real time, the response is returned in JS format.

3-5. Creating a view

It's finally time to create a view.

First of all, let's edit the view screen of the post list ... I would like to say, but first define the method to be used in the view.

Please add the code as follows to the Tweet model.

app>models>tweet.rb


class Tweet < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy

  #Additional part(liked_by method)
  def liked_by(user)
    Like.find_by(user_id: user.id, tweet_id: id)
  end
  #Additional part

end

The `liked_by``` method added above looks for a match that matches ```user_id``` and `tweet_id``` and returns nill if it doesn't exist.

Then, add the following code to app / views / tweets / index.html.erb.

html:app>views>tweets>index.html.erb



<% @tweets.each do |tweet| %>

  #Add to the part where you want to display the like button
  <div class="content-like">
    <ul class="content-like__icons">
      <li id="<%= tweet.id.to_s %>">
        <% if tweet.liked_by(current_user).present? %>
          <%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %>
            <i class="far fa-thumbs-up"></i>
          <% end %>
        <% else %>
          <%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %>
            <i class="far fa-thumbs-up"></i>
          <% end %>
        <% end %>
      </li>
    </ul>
  </div>
  #This is the end of the additional part

<% end %>

liked_byAs an argument tocurrent_user By passing, the currently logged in user can post"How nice"I'm judging if I'm doing it.

Now if you click the "Like button" when the user is not "Like", the `create``` action you created earlier will be executed, and when the user is "Like", I was able to execute a `destroy``` action and make a conditional branch.

Don't forget to add the `remote: true``` option to link_to``` as you need to call the `` .js.erb``` file when the link is pressed. Please give me.

For the "Like Button" icon, Font Awesome is used. For the introduction method, I think that the following qiita article etc. will be helpful. rails font-awesome-sass installation method

Next, create a file to output when the create action is executed.

app/Create a likes folder directly under the views folder and create in it.js.Create an erb file.



 After creating the file, add the code as follows.


#### **`ruby:app>views>likes>create.js.erb`**

$('#<%= @tweet.id.to_s %>'). html('<%= j render "tweets/liked", { tweet: @tweet } %>');

 The above code calls the `` `_liked.html.erb``` file in the `` `tweets``` folder when the create action is executed.


#### **`In the tweets folder_liked.html.Create an erb file and add the following code.`**

html:app>views>tweets>_liked.html.erb


<%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %>
  <i class="far fa-thumbs-up"></i>
<% end %>

In the above code, when I press the "Like button", the HTML that cancels the "Like" is displayed.

In the same way, let's create a file that will be called when the destroy action is executed.

app/views>destroy in the likes folder.js.Create an erb file.



 After creating the file, add the code as follows.


#### **`ruby:app>views>likes>destroy.js.erb`**

$('#<%= @tweet.id.to_s %>'). html('<%= j render "tweets/like", { tweet: @tweet } %>');



#### **`In the tweets folder_like.html.Create an erb file and add the following code.`**

html:app>views>tweets>_like.html.erb


<%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %>
  <i class="far fa-thumbs-up"></i>
<% end %>

With the above, we were able to implement the like function asynchronously.

The rest is how it looks, but the class name is `like``` when it is liked, and `like``` when it is not, so try customizing it with CSS to your liking. Please give me.

In my case, I change the color of the icon to red when it is liked and gray when it is not liked, as follows.

app>assets>stylesheets>tweets>_tweet.css


.like {
  color: gray;
}

.liked {
  color: red;
}

4. Finally

This is my first post, but writing an article is harder than I had imagined. I would appreciate it if you could point out any parts that are difficult to understand or incorrect.

Thank you for reading to the end ☺️

Recommended Posts

[Rails] Asynchronous implementation of like function
[Rails] Implementation of like function
[Rails] About implementation of like function
[Rails 6] Like function (synchronous → asynchronous) implementation
[Ruby on rails] Implementation of like function
Implementation of like function (Ajax)
[Rails 6] Implementation of search function
[Rails] Implementation of category function
[Rails] Implementation of tutorial function
[Rails] Implementation of CSV import function
[Rails] Implementation of image preview function
[Rails] Implementation of user withdrawal function
[Rails] Implementation of CSV export function
Implementation of like function in Java
Rails sorting function implementation (displayed in order of number of like)
Rails [For beginners] Implementation of comment function
[Rails 6] Implementation of SNS (Twitter) sharing function
[Vue.js] Implementation of menu function Implementation version rails6
[Vue.js] Implementation of menu function Vue.js introduction rails6
Implementation of search function
Rails search function implementation
Implementation of pagination function
[Rails] [jQuery] Asynchronous like function implementation using remote: true and js.erb
Implementation of Ruby on Rails login function (Session)
[Rails 6] Implementation of inquiry function using Action Mailer
[Rails] Implementation of image enlargement function using lightbox2
[Rails] Implementation of retweet function in SNS application
[Rails, JavaScript] Implemented like function (synchronous / asynchronous communication)
Rails fuzzy search function implementation
Implementation of sequential search function
Implementation of image preview function
Implementation of category pull-down function
Login function implementation with rails
[Rails 6] Pagination function implementation (kaminari)
Ruby on Rails <2021> Implementation of simple login function (form_with)
[Ruby on Rails] Asynchronous communication of posting function, ajax
[Rails] Implementation of drag and drop function (with effect)
Implementation of Ruby on Rails login function (devise edition)
[Ruby on Rails] Implementation of tagging function/tag filtering function
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
[Rails] Implementation of SNS authentication (Twitter, Facebook, Google) function
[Rails] Implementation of user logic deletion
[Rails] Implementation of multi-layer category function using ancestry "Editing form"
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
Kaminari --Added pagination function of Rails
Implementation of asynchronous processing in Tomcat
[Rails] Implementation of tagging function using intermediate table (without Gem)
[Rails] Implementation of many-to-many category functions
[Rails] gem ancestry category function implementation
[Ruby on Rails] Comment function implementation
[Rails] Comment function implementation procedure memo
[Rails] Implementation of new registration function in wizard format using devise
I tried to implement Ajax processing of like function in Rails
[Rails 6] Implementation of new registration function by SNS authentication (Facebook, Google)
[Rails] Implementation of coupon function (with automatic deletion function using batch processing)
[Rails] Implementation of tag function using acts-as-taggable-on and tag input completion function using tag-it
[Rails] Addition of Ruby On Rails comment function
Implementation of user authentication function using devise (2)
Rails Addition of easy and easy login function
Implementation of multi-tenant asynchronous processing in Tomcat