Week 2 of our Makers Academy engineering project has commenced. We have had a great experience so far with rails and now feel like we are getting to grips with the more complicated aspects. This guide is a quick introduction into adding likes to posts.
Create the controller and model
First you will need to generate the model:
rails g model Like post:references user:references
Then, migrate the table:
rails db:migrate
And finally, add the controller:
rails g controller likes
Add the create method to the controller
class LikesController < ApplicationController
before_action :find_post def create
@post.likes.create(user_id: current_user.id)
redirect_to post_path(@post)
end private def find_post
@post = Post.find(params[:post_id])
endend
In app/views/posts/show.html.erb
add this code:
<%= button_to 'Like', post_likes_path(@post), method: :post %>
In routes.rb
nest the comments resource inside the posts resource:
resources :posts do
resources :likes
end
Now establish the one to many relationship between posts/users and likes. You do this like so:
In app/models/post.rb
and app/models/user.rb
add the line:
has_many :likes, dependent: :destroy
The first part gives the relation between the posts/users and their likes. The second part makes sure that when you delete a post/user, all their likes get deleted.
In app/views/posts/show.html.erb
add this line to display how many likes a post has:
<p><%= @post.likes.count %> <%= (@post.likes.count) == 1 ? 'Like' : 'Likes'%></p>
Then, in app/views/posts/index.html.erb
add this line to display how many likes a post has in the homepage:
<p><%= post.likes.count %> <%= (post.likes.count) == 1 ? 'Like' : 'Likes'%></p>
This is to show the number of likes. The bit of logic afterwards is to change to Like or Likes depending on the number.
Stop people liking more than once
First we need to add a method to check if a user has already liked a photo. Under the word private, add:
def already_liked?
Like.where(user_id: current_user.id, post_id:
params[:post_id]).exists?
end
Then edit your create method to account for it
def create
if already_liked?
flash[:notice] = "You can't like more than once"
else
@post.likes.create(user_id: current_user.id)
end
redirect_to post_path(@post)
end
The ‘if’ statement checks if there is a like in the database with the current user_id
and the current post_id
, which means they must have already liked. If so we don’t add another like and flash an error message.
Adding the unlike feature
In the likes controller we need to add a destroy method:
def destroy
if !(already_liked?)
flash[:notice] = "Cannot unlike"
else
@like.destroy
end
redirect_to post_path(@post)
end
In this method I also make sure they don’t unlike when they haven’t liked.
In order to delete a like you need to find the like. Therefore, we need to add a find_like method.
def find_like
@like = @post.likes.find(params[:id])
end
And make sure this method is called on the destroy method. Put this at the top of the controller:
before_action :find_like, only: [:destroy]
The only thing to do now is add an unlike button and make sure it shows up if you click like:
<% pre_like = @post.likes.find { |like| like.user_id == current_user.id} %>
<% if pre_like %>
<%= button_to 'Unlike', post_like_path(@post, pre_like), method: :delete %>
<% else %>
<%= button_to 'Like', post_likes_path(@post), method: :post %>
<% end %>
I first start with finding the like with the user id. If there is one I show the Unlike button and if not, I render the like button. When the user clicks delete, I pass in the post and the like, which it finds using find_post
and find_like
and then destroys the like.