0
votes

In my messages controller I wish to build a message that belongs_to a @sender (class Character). However, messages belong_to not only Characters but also Conversations, and this is causing the problem. The build method of the message needs to be passed the Conversation id to which it belongs so the message can pass validations. My code successfully finds the @conversation via an sql search, but how do I correctly pass both the @conversation.id and the message_params to the @sender.messages.build as strong parameters?

messages_controller.rb

def create
  @sender    = Character.find_by(callsign: params[:callsign])
  @recipient = Character.find_by(callsign: params[:recipient])
  sender_id    = @sender.id
  recipient_id = @recipient.id
  conversationID = Conversation.find_by_sql("
            SELECT senderConversations.conversation_id FROM Chats AS senderConversations
            INNER JOIN Chats AS recipientConversations
                ON senderConversations.conversation_id=recipientConversations.conversation_id
            WHERE senderConversations.character_id='#{sender_id}'
                AND recipientConversations.character_id='#{recipient_id}'
            GROUP BY senderConversations.conversation_id
            HAVING count(distinct senderConversations.character_id) = 2
  ; ")
  @conversation = Conversation.find_by(id: conversationID)
  if(@conversation.nil?)
    @conversation = @sender.conversations.create
    @recipient.conversations << @conversation
  end # It all works great up to here!
  @message = @sender.messages.build(message_params, @conversation) # Can't get this working
  if @message.save
    @conversation.messages << @message
    respond_to do |format|
      format.html do
      end
      format.js do
      end
    end
  else
    redirect_to request.referrer || root_url
  end
end

private

  def message_params
    params.require(:message).permit( :content, :picture )
  end

character.rb

has_many :chats,  foreign_key: "character_id",
                  dependent: :destroy
has_many :conversations, through: :chats, source: :conversation
has_many :messages

conversation.rb

has_many :messages

chat.rb

belongs_to :character
belongs_to :conversation

message.rb

belongs_to :character
belongs_to :conversation
validates :character_id, presence: true
validates :conversation_id, presence: true
1

1 Answers

1
votes

To add conversation to the message, try

@message = @sender.messages.build(message_params.merge(:conversation => @conversation))

A couple suggestions -

First, move all the querying code to the Message model. Given the params you're setting, the sender and recipient, you should be able to find or create the conversation and attach it correctly. Reduce the complexity of the controller.

Also, try to move that sql into a rails query if you can and set a scope. Not sure I completely understand the schema, but something like...

class Conversation < ActiveRecord::Base

  scope :between, (sender, recipient) -> { joins(:chats).where(:charcter_id => [sender, recipient]).group('chats.conversations_id').having("count(distinct senderConversations.character_id) = 2") }

Then you can look for the conversation on a before_save or before_validation callback, ensuring the conversation exists. In a scope, you'll be able to reuse the sql more easily to find the conversation in other situations.