Reputation: 11098
Basically I have a page that lists 10 most recent microposts. Each post has a like button. When this like button is clicked the likes table in my database is updated.
Likes table:
+----+------------+--------------+-------------------------+-------------------------+---------+
| id | likable_id | likable_type | created_at | updated_at | user_id |
+----+------------+--------------+-------------------------+-------------------------+---------+
| 2 | 5770 | Micropost | 2012-06-09 11:30:55 UTC | 2012-06-09 11:30:55 UTC | 2 |
| 3 | 5770 | Micropost | 2012-06-09 11:42:45 UTC | 2012-06-09 11:42:45 UTC | 2 |
+----+------------+--------------+-------------------------+-------------------------+---------+
A user must only be able to like a micropost once. I can make this possible with some jquery/js by displaying an unlike button that points to a destroy path when ever a micropost is liked.
But is there a way to do this server side too? Like not allow a micropost to be liked more than once by any means necessary? So if I was to go into rails console and try to manually like a micropost I already liked it wouldn't work because it would see that I had already liked the micropost?
Like model:
class Like < ActiveRecord::Base
belongs_to :likable, :polymorphic => true
attr_accessible :likable_id, :likable_type, :user_id
end
Micropost model:
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :likes, :as => :likable
end
Likes controller:
class LikesController < ApplicationController
def create
micropost = Micropost.find(params[:micropost])
like = micropost.likes.build(:user_id => current_user.id)
like.save
end
end
Likes form:
<%= form_tag likes_path, :remote => true, :class => "like_micropost" do %>
<%= hidden_field_tag :micropost, micropost.id %>
<%= submit_tag '', :class => "likeMicropostSubmit" %>
<% end %>
I previous tried this with no luck:
class LikesController < ApplicationController
def create
micropost = Micropost.find(params[:micropost])
if micropost.likes.where(:user_id => current_user.id).nil?
like = micropost.likes.build(:user_id => current_user.id)
like.save
end
end
end
Kind regards
Upvotes: 1
Views: 1472
Reputation: 8517
I would do two things:
So:
class LikesController < ApplicationController
def create
micropost = Micropost.find(params[:micropost])
current_user.likes! micropost
end
end
class Like < ActiveRecord::Base
validates_uniqueness_of :user_id, :scope => [:likable_id, :likable_type]
end
class User < ActiveRecord::Base
# exclamation mark to avoid confusion between things that are liked by the user
def likes!(likable_object)
unless likable_object.likes.where(:user_id => self.id).exists?
like = likable_object.likes.build(:user_id => self.id)
like.save
end
end
end
EDIT: Explanation:
1) Managing the Like creation phase is a model duty -> move the code that manages the creation to the model
2) Find a good method name for describe the action -> User.likes! something
sounds good
3) Implement the creation method ->
def likes!(likable_object)
like = likable_object.likes.build(:user_id => self.id)
like.save
end
4) Add validation in order to manage the model valid/model invalid logic -> class Like; validates_uniqueness_of ...
5) Make the likes!
method conscious of the valid/invalid logic ->
def likes!(likable_object)
unless likable_object.likes.where(:user_id => self.id).exists?
like = likable_object.likes.build(:user_id => self.id)
like.save
end
end
Upvotes: 0
Reputation: 15781
# in Like class
validates_uniqueness_of :user_id, :scope => [:likable_id, :likable_type]
This way you'll get validation error on trying to like something more than once.
Upvotes: 5
Reputation: 11098
Ok this worked for me:
class LikesController < ApplicationController
def create
micropost = Micropost.find(params[:micropost])
unless micropost.likes.where(:user_id => current_user.id).exists?
like = micropost.likes.build(:user_id => current_user.id)
like.save
end
end
end
Not having a create template is what was stopping it from working properly but the template is empty and un-used so wondering why it is mandatory.
Upvotes: -1