Rails: Favoriting System - models associations - ruby-on-rails

I have the following model associations setup were a consumer can favorite a product or a variant. I just wanted to ask if my approach is correct?
class Favorite < ApplicationRecord
belongs_to :consumer
belongs_to :favorited, polymorphic: true
belongs_to :product, optional: true
belongs_to :variant, optional: true
end
class Consumer < ApplicationRecord
has_many :favorites
has_many :favorite_products, through: :favorites, source: :favorited, source_type: 'Product'
has_many :favorite_variants, through: :favorites, source: :favorited, source_type: 'Variant'
end
class Product < ApplicationRecord
has_many :favorites, dependent: :destroy
end
class Variant < ApplicationRecord
has_many :favorites, dependent: :destroy
end
class CreateFavorites < ActiveRecord::Migration[6.0]
def change
create_table :favorites do |t|
t.references :consumer, index: true
t.references :favorited, polymorphic: true, index: true
t.integer :product_id
t.integer :variant_id
t.timestamps
end
end
end

It is not clear why you have both polymorphic favorited and separate relation on product_id/variant_id. Usually one would go with one of these approaches.
Polymorphic associations can be unidirectional too - has_many :favorites, as: :favorited, dependent: :destroy
If you go with separate columns for each relation - indexes on product_id and variant_id may also be useful to prevent full table scan on product/variant deletion (on dependent: :destroy, also if you do not expect callbacks/nested relations there - delete_all is faster).

Related

Create a relationship that allows a user to follow multiple types of records

I am currently working on a system that allows a user to follow different records (funds or wallets) but I am blocking on relationships.
I have three models: User, Fund and Wallet.
A user must be able to follow to multiple funds or wallets, and a fund or wallet must be able to have multiple followers.
The goal is to be able to do user.follows which would give me the list of records he follows (funds and wallets), and to do fund.followers or wallet.followers and get the list of users who follow these records.
I have created this table:
class CreateFollows < ActiveRecord::Migration[6.0]
def change
create_table :follows do |t|
t.integer :user_id
t.integer :followable_id
t.string :followable_type
t.timestamps
end
end
end
then I did:
class User < ApplicationRecord
has_many :follows, dependent: :destroy
end
class Follow < ApplicationRecord
belongs_to :follower, foreign_key: :user_id, class_name: 'User', inverse_of: :follows
belongs_to :followable, polymorphic: true
end
and this concern that I added to Fund and Wallet models:
module Followable
extend ActiveSupport::Concern
included do
has_many :followers, dependent: :destroy
end
end
I know it can't work as it is now, but I don't really understand what to put in the User model and in my concern. Can someone show me the path please?
I finally did the following:
class Follow < ApplicationRecord
belongs_to :follower, foreign_key: :user_id, class_name: 'User', inverse_of: :follows
belongs_to :followable, polymorphic: true
end
class User < ApplicationRecord
has_many :follows, dependent: :destroy
has_many :funds_followings, through: :follows, source: :followable, source_type: 'Fund'
has_many :wallets_followings, through: :follows, source: :followable, source_type: 'Wallet'
def followings
funds_followings + wallets_followings
end
end
module Followable
extend ActiveSupport::Concern
included do
has_many :follows, foreign_key: :followable_id, dependent: :destroy, inverse_of: :followable
has_many :followers, through: :follows, as: :followable
end
end
So I'm able to do everything I wanted.

Rails: How to handle associations among namespaced models?

I am trying to figure out what's the best way to handle namespaced models. Here's the models that i have in my project:
class Member < ApplicationRecord
has_one :ledger, inverse_of: :member, class_name: "Member::Ledger", dependent: :destroy
has_many :ledger_entries, through: :ledger
end
class Member::Ledger < ApplicationRecord
belongs_to :member, inverse_of: :ledger
has_many :ledger_entries, foreign_key: "member_ledger_id", dependent: :destroy
end
class Member::LedgerEntry < ApplicationRecord
belongs_to :ledger, foreign_key: "member_ledger_id"
end
And here's how my migrations files look like:
create_table :members do |t|
t.timestamps
end
create_table :member_ledgers do |t|
t.references :member, foreign_key: true, null: false, index: { unique: true }
t.timestamps
end
create_table :member_ledger_entries do |t|
t.references :member_ledger, foreign_key: true, null: false
t.timestamps
end
So I have few questions here:
Are migration files correct? I mean should i have member_ledger_id in the member_ledger_entries table or just ledger_id?
Are associations defined in a correct way? Even though this works but i am not sure this is how we are supposed to proceed.
I am using ruby-2.5.1 and rails-5.2.0.
Any help would be appreciated. Thanks in advance !!
Perhaps your associations could look more like:
class Member < ApplicationRecord
has_one :member_ledger, inverse_of: :member, dependent: :destroy
has_many :member_ledger_entries, through: :member_ledger
end
class Member::Ledger < ApplicationRecord
belongs_to :member, inverse_of: :member_ledger
has_many :member_ledger_entries, dependent: :destroy
end
class Member::LedgerEntry < ApplicationRecord
belongs_to :member_ledger
end

Rails polymorphic comments associate

Trying to work out an polymorphic association where Comments can belong to, for example, Photos and Users. Where a Comment on a user is treated as a "direct message". But I'm getting the User association a bit messed up.
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable
end
This is incorrect. Ideally, user.comments should retrieve all Comment records where user_id == user.id and something like user.messages should retrieve all Comments where the type is User and they are the subject.
Relationships:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
end
class Photo < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :messages, as: :commentable, class_name: 'Comment'
end
Schema:
# Comments
...
t.integer :user_id
t.integer :commentable_id
t.string :commentable_type
...
Then you can invoke:
#user.comments # Get all comments created by particular user
#user.messages # Get all comments where particular user is a subject
have you added the foreign key and type columns to the comments table? within a migration file:
def change
add_column :comments, :commentable_type, :integer
add_column, :commentable_type, :string
add_index :comments, [:commentable_type, :commentable_id]
end
Also make sure you have a Message model and it has the associations
class Message < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :commentable, polymorphic: true
end

Is there a way to have two join tables associate the same two classes in a rails application?

Long time listener, first time caller. I'm trying to create two associations between the same database tables, Chatrooms and Users. What I have so far is a has_many through relationship where a Chatroom has many Users through Messages. This part works fine. What I want to do is to create a second join table that connects Chatrooms to Users, through a join table called Chatroom_players. So what I'd like is for Chatroom.first.users to get me users through the messages join table and Chatroom.first.players to get me everyone from the chatroom_players join table. The reason I want this is so that I can maintain user presence even if a user hasn't written any messages in the chat, also so that a user can leave the room but maintain his or her messages in the chat.
Here is what I have so far that does not work:
chatroom.rb:
class Chatroom < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :users, through: :messages
has_many :chatroom_players
has_many :users, through: :chatroom_players
end
message.rb:
class Message < ApplicationRecord
belongs_to :chatroom
belongs_to :user
validates :content, presence: true, length: {minimum: 2, maximum: 200}
end
chatroom_player.rb
class ChatroomPlayer < ApplicationRecord
belongs_to :chatroom
belongs_to :user
end
user.rb
class User < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :chatrooms, through: :messages
has_many :chatroom_players
has_many :chatrooms, through: :chatroom_players
end
chatroom_players migration:
class AddChatroomPlayers < ActiveRecord::Migration[5.0]
def change
create_table :chatroom_players do |t|
t.references :user, index: true, foreign_key: true, null: false
t.references :chatroom, index: true, foreign_key: true, null: false
t.boolean :creator, default: false
t.timestamps null: false
end
end
end
You need to use different names for the associations:
class Chatroom < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :users, through: :messages
has_many :chatroom_players
# this is a separate association to users through the
# chatroom_players table.
has_many :participants,
through: :chatroom_players,
source: :user, # what association on chatroom_players to use
class_name: 'User' # since it cannot be deduced automatically
end

Get all related instances from has_many through relationship of a polymorphic class

I'm trying to create way for Users to Like content in my project. To do this I set up a polymorphic relationship as follows
Likes table
table "likes", force: :cascade do |t|
t.integer "likeable_id"
t.string "likeable_type"
t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
models
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :newsletters, through: :likes, source: :likeable, source_type: "newsletter"
has_many :gift_cards, through: :likes, source: :likeable, source_type: "gift_card"
has_many :stories, through: :likes, source: :likeable, source_type: "story"
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
end
class Newsletter < ActiveRecord::Base
has_one :like, as: :likeable, dependent: :destroy
has_one :user, through: :like, source: :likeable
end
class GiftCard < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes, source: :likeable
end
class Story < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes, source: :likeable
end
tldr: a User has many Likes and many Newsletters, GiftCards, and Stories through those Likes.
My question, with this structure, whats the best way to get everything a User has liked?
For me this is extremely hard to follow for a domain with is in reality is quite straightforwards. Perhaps you have a bigger reason to implement it is this way but here's my suggestion for the User Like and GiftCard models (which may or may not help!).
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :liked_giftcards, through: :likes, source: :giftcard
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :giftcard
end
class GiftCard < ActiveRecord::Base
has_many :likes, dependent: :destroy
end
class Story < ActiveRecord::Base
...same as GiftCard
end
Personally I would find this easier to follow. It provides easy access to everything a user has liked through user.likes and allows you to look at each category individually through user.liked_giftcards etc.
Ended up figuring it out. Just had some small syntactical errors
class User < ActiveRecord::Base
has_many :likes, dependent: :destroy
has_many :newsletters, through: :likes, source: :likeable, source_type: "Newsletter"
has_many :gift_cards, through: :likes, source: :likeable, source_type: "GiftCard"
has_many :stories, through: :likes, source: :likeable, source_type: "Story"
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :likeable, polymorphic: true
end
class Newsletter < ActiveRecord::Base
has_one :like, as: :likeable, dependent: :destroy
has_one :user, through: :like
end
class GiftCard < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes
end
class Story < ActiveRecord::Base
has_many :likes, as: :likeable, dependent: :destroy
has_many :users, through: :likes
end

Resources