As shown in the demo video below, we will implement a function that allows you to like the content posted by the user.
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.
** ■ 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.
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_idand
user_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.
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.
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_many
Indicates 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_to
Ishas_many
The 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.
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.
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.
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.
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.
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.
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.
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_by
As 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;
}
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