Adding belongs to relationship to Ruby Gem Mailbox

2019-02-11 02:52发布

问题:

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.

回答1:

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 %>


回答2:

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...



回答3:

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.



回答4:

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