Here is an account of my first use of Rails 1.1’s polymorphism in Model associations.
I had looked at all the examples at the wiki and a couple of other blogs but still wasn’t clear.
You appreciate most, the problems that you solve yourselves 🙂
Here is the problem I solved using Polymorphic associations.
Consider the scenario where there are Users of an application; who can act as both buyers and providers. Each user can infact have multiple buyer and provider profiles.
The application also has a messaging system for users to contact each other.
As a part of the application requirement, we need to keep track of the profiles of the message sender and receiver.
A user logs in using the login name and password present in the User model. I am omitting details so please assume for this example that each logged in user, has either a buyer profile or a provider profile associated to it, when they use the messaging system.
Here are the model files
A user can have many buyer_profiles and many provider_profiles
class User < ActiveRecord::Base
has_many :buyer_profiles
has_many :provider_profiles
end
A buyer profile belongs to a user and can have many messages associated to it as a sender and as a receiver.
class BuyerProfile < ActiveRecord::Base
belongs_to :user
has_many :messages, :as => :sender
has_many :messages, :as => :receiver
end
A provider profile also belongs to a user, and can have many messages associated to it as a sender and as a receiver.
class ProviderProfile < ActiveRecord::Base
belongs_to :user
has_many :messages, :as => :sender
has_many :messages, :as => :receiver
end
The message model holds the polymorphic magic, via the two interfaces sender and receiver. A message belongs to sender which can be polymorphic,either a buyer or a provider. And also a message belongs to a receiver, which again is polymorphic, either a provider or a buyer.
class Message < ActiveRecord::Base
belongs_to :sender, :polymorphic => true
belongs_to :receiver, :polymorphic => true
end
The code above requires that I have these four special fields in my messages table to handle polymorphism. Here is the migration for messages
class CreateMessagesTable < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.column :sender_id, :integer
t.column :sender_type, :string
t.column :receiver_id, :integer
t.column :receiver_type, :string
t.column :subject, :string
t.column :message, :string
end
end
def self.down
drop_table :messages
end
end
I ran the following code on the console to check if everything is working as expected.
r = BuyerProfile.find(1)
s = ProviderProfile.find(1)
m = Message.new
m.sender = s
m.receiver = r
m.subject = "test subject"
m.message = "test message"
m.save!
And it worked 🙂
I could access profles and users from the message object like this.
m = Message.find(1)
m.receiver # refer to the receivers profile object
m.receiver.class # get receiver type, where BuyerProfile or SellerProfile
m.sender # refers to the sender profile object
m.sender.class # get sender type, where BuyerProfile or SellerProfile
m.receiver.user # refer the User object of the message receiver
m.sender.user # refer the User object of the message sender
Polymorphism saved me a lot of time and also a whole new table which I was thinking of adding to handle this functionality prior to polymorphism.
Added : And yes, now I am thinking about what I would need to write to get all the messages sent or received by a User.
Hi Manik, this is very informative post and cleared couple of doubts I had.
>> Added : And yes, now I am thinking about what I would need to write to get all the messages sent or received by a User.
Hi, have you already found out how to do this?
Thanks for this great post Manik! I’m going to try and run with it and see what happens.
Tal
Incredibly helpful. Thanks for taking the time to write this up – it took all of five minutes to get this sort of thing working in my project. Much appreciated.
Checking spam
Great post. I was wondering how to delete a message from say a BuyerProfile object?
e.g.
bp = BuyerProfile.find :first
bp.messages.count # outputs 2
bp.messages.first.destroy
Destroy should delete the first message, but it won’t do so. Any reason wjy?
Oops, I was trying this out in console… for some reason I need to reload the object to find that it’s gone.
i believe since the BuyerProfile and ProviderProfile are inherited from the activerecord base class it would mean that i need to create a table called buyerprofiles and providerprofiles which has a column called user_id
Please let me know if i am correct?
Great Example,
Thanks for illustrating this out in such simple fashion. Saved at least a couple of my hours 🙂
-Thanks
Holy crap – your blog post was the first to make sense to me! I know what polymorphism is but none of the examples made sense until I saw yours!
In my case, I have an item that has an owner, but the owner can be of differing types (Employee, Department, etc.).
Thanks!