Last active
February 26, 2018 19:04
-
-
Save lortza/330ba4887b04964a55ee0ea3c881e32f to your computer and use it in GitHub Desktop.
Setting up polymorphic "likes" on posts, photos, and comments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- views/shared/_likes.html.erb --> | |
<% if object.has_likes? %> | |
<p class='likes'> | |
Likes: <%= object.likes_count %> | |
<%= list_likes(object.likes) %>... | |
</p> | |
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Use concerns to DRY up model associations | |
# models/likes.rb | |
class Like < ApplicationRecord | |
# ... | |
belongs_to :likeable, polymorphic: true | |
# ... | |
end | |
# models/comment.rb | |
class Comment < ApplicationRecord | |
#... | |
belongs_to :commentable, polymorphic: true | |
include Liking | |
#... | |
end | |
# models/photo.rb | |
class Photo < ApplicationRecord | |
# ... | |
# use concerns to DRY up model associations | |
include Liking | |
include Commenting | |
# ... | |
end | |
# models/post.rb | |
class Post < ApplicationRecord | |
# ... | |
# use concerns to DRY up model associations | |
include Liking | |
include Commenting | |
# ... | |
end | |
# models/concerns/liking.rb | |
module Liking | |
extend ActiveSupport::Concern | |
included do | |
has_many :likes, as: :likeable | |
end | |
def has_likes? | |
likes.any? | |
end | |
def likes_count | |
likes.count | |
end | |
end | |
# models/concerns/commenting.rb | |
module Commenting | |
extend ActiveSupport::Concern | |
included do | |
has_many :comments, as: :commentable | |
end | |
def has_comments? | |
comments.any? | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# controllers/likes_controller.rb | |
class LikesController < ApplicationController | |
def create | |
session[:return_to] ||= request.referer | |
set_parent | |
# create the like as a child in the collection of the parent class | |
@like = @parent.likes.new(user_id: current_user.id) | |
if @like.save | |
redirect_to session.delete(:return_to) | |
else | |
flash[:error] = "Something went wrong." | |
redirect_to session.delete(:return_to) | |
end | |
end | |
def destroy | |
session[:return_to] ||= request.referer | |
set_parent | |
author = @parent.user | |
like = Like.find(params[:id]) | |
like.destroy | |
redirect_to session.delete(:return_to) | |
end | |
private | |
def like_params | |
params.require(:likeable).permit(:post_id, :comment_id, :user_id) | |
end | |
def set_parent | |
# Determine the parent class of the like by grabbing the text from the `likeable` param | |
klass_name = params[:likeable] | |
# Generate the name of the parent id param based on the likeable class | |
parent_key = params.keys.select{|k| k.match(klass_name.downcase + '_id')}.first | |
# Use the parent param name to get the value of the parent id | |
id = params[parent_key] | |
# Find the parent object by its id | |
@parent = klass_name.constantize.find(id) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# helpers/likes_helper.rb | |
module LikesHelper | |
def list_likes(likes) | |
# "likes" are limited to showing only 3, and the tricky part was displaying them as links that are comma separated | |
raw(likes.limit(3).map do |like| | |
link_to like.user.name, user_timeline_path(like.user) | |
end.join(", ")) | |
end | |
# This method handles displaying links to create/destroy objects of multiple classes | |
def display_like_unlike(object) | |
klass = object.class.to_s | |
like = object.likes.to_a.find { |l| l.user_id == current_user.id } | |
if like == nil | |
# Use polymorphic_url instead of a url helper to build the url for liking and unliking of objects of different classes. | |
# The `likeable: klass` will be used by the controller to set the like's parent | |
link_to 'Like', polymorphic_url([current_user, object, :likes], likeable: klass), method: :post | |
else | |
link_to 'Unlike', polymorphic_url([current_user, object, like], likeable: klass), method: :delete | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- views/timelines/show.html.erb --> | |
... | |
# an "activity" can be a text "post" or a "photo" post | |
<% @timeline.activities.each do |activity| %> | |
<!-- this syntax will automatically render the correct text or photo post based on its class --> | |
<%= render activity %> | |
<% end %> | |
<!-- And those partials are: --> | |
<!-- 1) views/photos/_photo.html.erb --> | |
<% unless photo.new_record? %> | |
<article class="activity"> | |
<%= render 'shared/activity_header', object: photo %> | |
<section class="activity-body"> | |
<%= image_tag photo.photo_data.url(:feed) %> | |
</section> | |
<!-- the "liking" functionality lives here in the footer --> | |
<%= render 'shared/activity_footer', object: photo %> | |
</article> | |
<% end %> | |
<!-- 2) views/posts/_post.html.erb --> | |
<% unless post.new_record? %> | |
<article class="activity"> | |
<%= render 'shared/activity_header', object: post %> | |
<section class="activity-body"> | |
<p><%= post.body %></p> | |
</section> | |
<!-- the "liking" functionality lives here in the footer --> | |
<%= render 'shared/activity_footer', object: post %> | |
</article> | |
<% end %> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment