How do you model Likes in rails?
To elaborate further on my comment to Brandon Tilley's answer, I would suggest the following:
class User < ActiveRecord::Base
# your original association
has_many :things
# the like associations
has_many :likes
has_many :liked_things, :through => :likes, :source => :thing
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :thing
end
class Thing < ActiveRecord::Base
# your original association
belongs_to :user
# the like associations
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
How to like a track and view all liked tracks on my Like index page in my rails app?
Yes, your logic is a bit weird here, you can simplify everything with the following:
- Drop the
Like
model and database table (not sure why do you want to attach a photo here?) - Change the
LikedTrack
to belong toUser
instead ofLike
- Rename the
LikedTrack
toLike
Now you only need to create a new Like record when user clicks the "heart" button, it should take the track_id
and user_id
as arguments behind your form.
Rails 5 adding Likes to Articles and Comments
I just figured it out, actually.
class Like < ApplicationRecord
belongs_to :user
belongs_to :article
# belongs_to :comment
end
Commenting the above out, it allows the @article "like" to save without a comment being referenced. Article likes are saved properly. However, article.likes.count still increments whenever an article.comment is liked. This means article.likes is always >= article.comments.likes; which is completely fine.
I just changed the @article.likes to:
<%= "#{@article.likes.where(comment_id: nil).count}" %>
Filtering out all the strictly article.likes. The comment.likes still work perfectly.
Liking a comment in rails
You should use Polymorphic Association here. But I notice that you have the wrong understanding of Polymorphic Association in the above explanation because you are referencing Blog
& Comment
via blog_id
& comment_id
respectively in the Like
model instead of referencing those through likeable
(i.e., likeable_id
& likeable_type
). Follow the below steps for better understanding.
- Generate
Like
model
rails g model Like user:references likeable:references{polymorphic}
Like
model migration should look like this
class CreateLikes < ActiveRecord::Migration[6.1]
def change
create_table :likes do |t|
t.references :user, null: false, foreign_key: true
t.references :likeable, polymorphic: true, null: false
t.timestamps
end
end
end
- Like model
class Like < ApplicationRecord
belongs_to :user
belongs_to :likeable, polymorphic: true
end
- Blog model
class Blog < ApplicationRecord
has_many :likes, as: :likeable
end
- Comment model
class Comment < ApplicationRecord
has_many :likes, as: :likeable
end
- Table entries of
Like
model should look similar to this
=> #<Like:0x00# id: 1, user_id: 1, likeable_id: 1, likeable_type: "Blog", created_at: <timestamp>, updated_at: <timestamp>>
=> #<Like:0x00# id: 2, user_id: 1, likeable_id: 2, likeable_type: "Comment", created_at: <timestamp>, updated_at: <timestamp>>
Building a form for a like button in a polymorphic relationship in Rails
This should work:
= form_for([@post, @post.likes.find_by(user_id: current_user.id)], html: {method: :delete}) do |f|
url: post_like_path(@post)
in your code expects a second argument (the like
object). That's what throws the error.
But you don't need it actualy if you put the nested resources as an array in the first argument of the form_for
helper.
If the record passed to form_for is a resource, i.e. it corresponds to
a set of RESTful routes, e.g. defined using the resources method in
config/routes.rb. In this case Rails will simply infer the appropriate
URL from the record itself.
(source: http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for)
You can pass an array of resources if your resource is nested within another resource.
Now... You probably want to re-use this code for your other polymorphic models as well. You can do this by passing @post
, or @comment
to your partial like this:
= render :partial => 'like_button', locals: {likable: @post}
and refactor your partial like this:
= form_for([likable, likable.likes.find_by(user_id: current_user.id)], html: { method: :delete}) do |form|
Limit user to 1 like?
I think you could create additional model:
class CommentLike
belongs_to :comment
belongs_to :user
validates :user, uniqueness: { scope: :comment}
end
class User
has_many :comment_likes
end
class Comment
has_many :comment_likes
end
def like
@comment = Comment.find(params[:id])
if current_user.comment_likes.create(comment: @comment)
@comment.increment!(:likes)
@comment.create_activity :like
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
It'll solves you problem. If storing likes_count in comment is neccessary, you could use Rails' counter_cache.
How to get post with maximum likes or post with likes counts in rails
Use counter_cache
so that you always have a count of likes on the Post
objects, then you can call Post.maximum(:likes_count).first
to retrieve the one post that has the most likes. Likewise, any Post
query will include a post's like count.
Related Topics
Rails 5.2.0 with Ruby 2.5.1 Console - 'Warning:' 'Already' Initialized Constant Fileutils::Version
Ruby - Bundle Install/Update Too Slow
Ubuntu 12 - How to Install Ruby and Rails Correctly
Using Ruby's Optionparser to Parse Sub-Commands
How to Use Capybara in Pure Ruby (Without Rails)
How to Convert an Existing Rails 3 Application into an Engine
How to Solve the Update Bundler Warning in Rails When Deploying to Heroku
Finding All the Users That Have Duplicate Names
Restarting Unicorn with Usr2 Doesn't Seem to Reload Production.Rb Settings
How to Install Rvm Without Root Access
Group_By in Rails by 2 or More Attributes
What Is the Preferred Way (Better Style) to Name a Namespace in Ruby? Singular or Plural
$ Bundle Exec Rake Db:Reset Command Raising Couldn't Drop Db/Development.Sqlite3
Throttling Requests to a Ruby on Rails API
How to Sort a Hash by Value in Descending Order and Output a Hash in Ruby