I am building an e-com application and would like to implement something like a messaging system. In the application, all conversation will be related to either a Product
model or an Order
model. In that case, I would like to store the relating object (type + id, I supposed) to the Conversation
object.
To add the fields, of course I can generate and run a migration, however, since the Model and Controller are included within the gem, how can I declare the relationship? (belongs_to :linking_object, :polymorphic
) and the controller? Any idea?
Thank you.
I ended up customizing the Mailboxer gem to allow for a conversationable
object to be attached to a conversation.
In models/mailboxer/conversation.rb
belongs_to :conversationable, polymorphic: true
Add the migration to make polymorphic associations work:
add_column :mailboxer_conversations, :conversationable_id, :integer
add_column :mailboxer_conversations, :conversationable_type, :string
In lib/mailboxer/models/messageable.rb
you add the conversationable_object
to the parameters for send_message
:
def send_message(recipients, msg_body, subject, sanitize_text=true, attachment=nil, message_timestamp = Time.now, conversationable_object=nil)
convo = Mailboxer::ConversationBuilder.new({
:subject => subject,
:conversationable => conversationable_object,
:created_at => message_timestamp,
:updated_at => message_timestamp
}).build
message = Mailboxer::MessageBuilder.new({
:sender => self,
:conversation => convo,
:recipients => recipients,
:body => msg_body,
:subject => subject,
:attachment => attachment,
:created_at => message_timestamp,
:updated_at => message_timestamp
}).build
message.deliver false, sanitize_text
end
Then you can have conversations around objects:
class Pizza < ActiveRecord::Base
has_many :conversations, as: :conversationable, class_name: "::Mailboxer::Conversation"
...
end
class Photo < ActiveRecord::Base
has_many :conversations, as: :conversationable, class_name: "::Mailboxer::Conversation"
...
end
Assuming you have some users set up to message each other
bob = User.find(1)
joe = User.find(2)
pizza = Pizza.create(:name => "Bacon and Garlic")
bob.send_message(joe, "My Favorite", "Let's eat this", true, nil, Time.now, pizza)
Now inside your Message View you can refer to the object:
Pizza Name: <%= @message.conversation.conversationable.name %>
Although rewriting a custom Conversation system will be the best long-term solution providing the customization requirement (Like linking with other models for instance), to save some time at the moment I have implement the link with a ConversationLink
Model. I hope it would be useful for anyone in the future who are at my position.
Model: conversation_link.rb
class ConversationLink < ActiveRecord::Base
belongs_to :conversation
belongs_to :linkingObject, polymorphic: true
end
then in each models I target to link with the conversation
, I just add:
has_many :conversation_link, as: :linkingObject
This will only allow you to get the related conversation from the linking object, but the coding for reverse linking can be done via functions defined in a Module.
This is not a perfect solution, but at least I do not need to monkey patch the gem...
The gem automatically take care of this for you, as they have built a solution that any model in your own domain logic can act as a messagble object.
Simply declaring
acts_as_messagable
In your Order or Product model will accomplish what you are looking for.
You could just use something like:
form_helper :products
and add those fields to the message form
but mailboxer comes with attachment functionality(carrierwave) included
this might help if you need something like attachments in your messages:
https://stackoverflow.com/a/12199364/1230075