Question

I have a user-to-user messaging system. I'm trying to pass an array of user ids to a ConversationUser (join table) model which would then create multiple conversation_users from each individual user.id. The two fields in ConversationUser are conversation_id and user_id. I'm able to initialize a single conversation user because the new conversation_id is being passed along to the model, but for some reason, the hash of user ids is not getting to my model. I'm getting a Validation failed: User can't be blank

My conversation/new view for capturing the user_ids:

<%= check_box_tag "conversation_user[recipient][]", user.id %> <%= user.name %><br />

I know this is working because part of my params that I'm receiving back are:

"conversation_user"=>{"recipient"=>["9", "10"]}

The essentials of my Rails 4 controller & strong params:

class ConversationsController < ApplicationController
  def new
    @user = User.find(params[:user_id])
    @conversation = @user.conversation_users.build
    @conversation.build_conversation.messages.build
  end

  def create
    @conv = Conversation.create!
    @conversation = @conv.conversation_users.create!(conversation_user_params)
  end

  def conversation_user_params
    params.require(:conversation_user).permit(recipient: [])
  end

The essentials of my ConversationUser model:

class ConversationUser < ActiveRecord::Base
  attr_accessor :recipient

  before_create :acquire_conversation

  validates :user_id, :conversation_id, presence: true 

  def acquire_conversation
    unless recipient.blank?
      recipient.each do |u|
        ConversationUser.create(user_id: u, conversation: conversation)
      end
    end
  end
end

I think the problem is somewhere in my controller's conversation_user_params. But it also might be in the model's before_create method. I've been trying to fix this problem for a day now, with lots of debugging with no success. If anyone can be of assistance, I thank you in advance.

Was it helpful?

Solution

The problem is in the model. before_create callback is called before creating a ConversationUser. Let's name this created ConversationUser as CURRENT. So, before creating the CURRENT ConversationUser you loop through recipient ids and create a ConversationUser for each of them. The ConversationUsers that you are creating here are not CURRENT ConversationUser. CURRENT ConversationUser is saved after the callback is executed (after you create other ConversationUsers). But in this case CURRENT ConversationUser doesn't know wich User it belongs to, because you pass user_id parameter to ConversationUsers that you create in before_create callback, but you do not pass it to CURRENT ConversationUser when it is created (when original create! method is executed).

To solve this problem you can override original create! method or not use it at all for creating ConversationUsers by recipient ids. Add a new method to your Conversation model (for example create_conversation_users):

Solution

In the controller:

def create
  @conv = Conversation.create!
  @conversation = @conv.create_conversation_users!(conversation_user_params[:recipient])
end

In the model:

class Conversation
  def create_conversation_users!(recipient_ids)
    return if recipient_ids.blank?

    recipient_ids.each do |recipient_id|
      conversation_users.create!(user_id: recipient_id, conversation: self)
    end
  end
end

You should also update ConversationUser model:

class ConversationUser < ActiveRecord::Base
  validates :user_id, :conversation_id, presence: true 
end

OTHER TIPS

The error is in the ConversationUser. before_create callbacks are ran before a record is created in the database BUT after validations are ran. To solve your issue, there's a few things you can do. One of them was answered by Chumakoff. Here's another option you can use.

Remove all the code inside ConversationUser and change conversation_user_params to

def conversation_user_params
  params[:conversation_user][recipient].map do |recipient|
    { user_id: recipient }
  end
end

What happens is you're passing an array of { user_id: 1 } to create! which is the same as calling multiple create!({ user_id: 1 }).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top