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

Ticket:

DESCRIPTION

Posts and comments can be 'liked' and the number of likes is visible next to the post or comment

ACCEPTANCE CRITERIA

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

FURTHER CHALLENGE

  • 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

Resources used:

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:

Guide:

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 the likes? 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 in likes_controller.rb. If they haven't, they see a link to 'like' a post via a post request which will call the #create action in likes_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
⚠️ **GitHub.com Fallback** ⚠️