Search code examples
ruby-on-railsrubyfavorites

User to mark (favorite-like) another Model in Ruby on Rails


I want to implement a "Read Later" (just like Favorites) system in a Ruby on Rails app. What I want is for a User model to be able to mark a Content model to read later.

My associations between the two models are like this:

class User < ActiveRecord::Base
    has_many :contents
end

-------------

class Content < ActiveRecord::Base
    belongs_to :user
end

Then a Content belongs to a Category, etc, but that doesn't matter for the question so I just didn't put it there.

A User can mark a Content (that could belong to another user) and there will be a list of "marked contents (to read later)" for each user.

How could I implement this?

I've already read this question but I didn't really understand and when trying to simulate it, it didn't work.


Solution

  • What did you try and what didn't work?

    This is pretty straight forward. Let us think through:

    There is a User:

    class User < ActiveRecord::Base
    end
    

    There is Content:

    class Content < ActiveRecord::Base
    end
    

    A User can create Content and is he restricted to create only one content? no. A User can create as many contents as he wants. This is to say in Rails terms a User has_many contents. To put this in other words, can we say that a Content is created by a User.

    class User < ActiveRecord::Base
      has_many :contents
    end
    
    class Content < ActiveRecored::Base
      belongs_to :user
    end
    

    Now, the content (typically created by other users) can be favorited (marked as 'Read Later') by other users. Each User can favorite (mark 'Read Later') as many contents as he wants and each Content can be favorited by many users isn't it? However, we'll have to track which User favorited which Content somewhere. The easiest would be to define another model, let us say MarkedContent, to hold this information. A has_many :through association is often used to set up a many-to-many connection with another model. So the relevant association declarations could look like this:

    class User < ActiveRecord::Base
      has_many :contents
    
      has_many :marked_contents
      has_many :markings, through: :marked_contents, source: :content
    end
    
    class MarkedContent < ActiveRecord::Base
      belongs_to :user
      belongs_to :content
    end
    
    class Content < ActiveRecord::Base
      belongs_to :user
    
      has_many :marked_contents
      has_many :marked_by, through: :marked_contents, source: :user
    end
    

    Now you can do:

    user.contents # to get all the content created by this user
    user.marked_contents # to get all the contents marked as 'Read Later' by this user
    
    content.user # to get the creator of this content
    content.marked_by # to get all the users who have marked this content
    

    Read more here to learn about associations.

    To mark a content as a favorite, one way would be:

    @user = User.first
    @content = Content.last
    
    @user.markings << @content 
    

    You can also implement a method in the User model to do this for you:

    class User < ActiveRecord::Base
      ...
      def read_later(content)
        markings << content
      end
    end
    

    Now, you can do:

    @user.read_later(@content)