Feature: Users can 'like' posts - NikitaDouglas/acebook-Kindred GitHub Wiki
This page lists the actions to implement a 'like' feature, as described in the following ticket, in our Kindred Acebook
Posts and comments can be 'liked' and the number of likes is visible next to the post or comment
A signed in user can:
- click on a 'like' emoji next to posts and comments to indicate a like, and can then click on it to remove that like.
- a count of likes shows next to the like emoji
- Users can like each post only once
- the browser should not refresh when liking posts - this feature could be added by adding in a JavaScript response to the #create and #destroy actions in the guide beow
The steps taken were primarily based around this guide. Some extra elements needed to be fleshed out (such as a likes
method in the post module) and other small alterations were made, detailed below:
Run rails g model Like user_id:integer post_id:integer
to create the Like model with associated migrations file containing instructions for a Likes table with user_id and post_id columns
Run rake db:migrate db:test:prepare
to create the 'likes' table in the development and test databases
To create the routes and resources for like nested within the post resources and routes:
-
In
routes.rb
:resources :posts do resource :like, module: :posts
This means that every like is specific to an individual post, and that the controllers and routes related to the Like model are scoped under posts
Add the following to app/controllers/posts/likes_controller.rb
:
def create
set_post
@post.likes.where(user_id: current_user.id, post_id: @post.id).first_or_create
redirect_to posts_url
end
end
private
def set_post
@post = Post.find(params[:post_id])
end
- Here we add a create action that identifies a post according to the url parameters, sets it equal to @post. We then search for a 'like' in the 'likes' table where its post_id matches @post's id and its user_id matches the current users's id. The
first_or_create
method means that if no such like exists we create one, if one already exists we create another. Afterwards the user is redirected back to the posts_url which displays all the posts
In app/views/posts/index.html.erb
use this template:
<% if user_signed_in? && current_user.likes?(post) %>
<%= link_to "Unlike", post_like_path(post), method: :delete %>
<% else %>
<%= link_to "Like", post_like_path(post), method: :post %>
<% end %>
- In order for this function to appear under every post we iterate over the results of Post.all, assigning each post to the variable
post
. The code uses thelikes?
method to check whether or not a user has already liked a post. If they have, they see a link to 'unlike' a post, via a delete request which will call the #destroy action inlikes_controller.rb
. If they haven't, they see a link to 'like' a post via a post request which will call the #create action inlikes_controller.rb
Add the likes?
method to User.rb:
has_many :likes
def likes?(post)
post.likes.where(user_id: id, post_id: post.id).any?
end
Add the likes
method to Post.rb:
def likes
Like.all
end
- Returning all likes within a method called on a Post object is a slightly backwards way to go about things and perhaps there is a better way to do this.
Finally, create the #destroy action within likes_controller.rb
:
def create
set_post
@post.likes.where(user_id: current_user.id, post_id: @post.id).first_or_create
redirect_to posts_url
end
end