Search code examples
ruby-on-railsrubyactiverecordstrong-parametersactioncontroller

Rails: passing params hash to model


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.


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