Search code examples
ruby-on-railsrubyruby-on-rails-5.2

update params hash in rails


I need a way to modify the params hash. I tried a couple of things but none of it actually worked.

Unpermitted parameters: :players, :leaders

Params

{"utf8"=>"✓", "authenticity_token"=>"asa", "user"=>{"first_name"=>"anikeeet", "last_name"=>"tiwariii", "friend_attributes"=>{"players"=>"50589,50590", "leaders"=>"16,6,18", "phone_number"=>"", "title"=>"aasdassd", "role"=>"abcdef"}}}

I tried this way

(friend_params.dig(:friend_attributes) || []).each do |_, attributes|
      attributes['players'] = attributes['players'].split(',').map(&:to_i)
      attributes['leaders'] = attributes['leaders'].split(',').map(&:to_i)
    end

this way

(friend_params.dig(:friend_attributes) || []).each do |key, val|
  friend_params["friend_attributes"]["#{key}"] = val.split(',').map(&:to_i) if key == 'players' || key == 'leaders'
end

this way

(params.dig(:user, :friend_attributes) || []).each do |key, val|
  params["user"]["friend_attributes"]["#{key}"] = val.split(',').map(&:to_i) if key == 'players' || key == 'leaders'
end

and this way

 if (attributes = params.dig(:user, :friend_attributes))
      %i[players leaders].each do |key|
        next unless attributes.has_key?(key)
        attributes[key] = attributes[key].split(',').map(&:to_i)
      end
    end

But none of the ways worked for me. I am sharing my full controller code

class FriendsController < ApplicationController

  def update
    # (friend_params.dig(:friend_attributes) || []).each do |_, attributes|
    #   attributes['players'] = attributes['players'].split(',').map(&:to_i)
    #   attributes['leaders'] = attributes['leaders'].split(',').map(&:to_i)
    # end

    # (friend_params.dig(:friend_attributes) || []).each do |key, val|
    #   friend_params["friend_attributes"]["#{key}"] = val.split(',').map(&:to_i) if key == 'players' || key == 'leaders'
    # end

    # (params.dig(:user, :friend_attributes) || []).each do |key, val|
    #   params["user"]["friend_attributes"]["#{key}"] = val.split(',').map(&:to_i) if key == 'players' || key == 'leaders'
    # end

     if (attributes = params.dig(:user, :friend_attributes))
       %i[players leaders].each do |key|
         next unless attributes.has_key?(key)
         attributes[key] = attributes[key].split(',').map(&:to_i)
       end
     end


    if current_user.update(friend_params)
      render json: { success: true }
    else
      render json: {error: "something went wrong"}
    end
  end

  private
  def friend_params
    params.require(:user).permit(:first_name, :last_name, friend_attributes: [:phone_number, :title, :role, :players, :leaders])
  end
end

Association

Class User < ApplicationRecord 
  has_one :friend, foreign_key: :user_id, dependent: :destroy
  accepts_nested_attributes_for :friend
end

class Friend < ApplicationRecord
  self.table_name = 'schema.friends'
  belongs_to :user
end

players and leaders are the array field stored in friends table Can anyone tell what I am doing wrong?


Solution

  • Something like this would do the job:

    if (attributes = params.dig(:user, :friend_attributes))
      %i[players leaders].each do |key|
        next unless attributes.has_key?(key)
        attributes[key] = attributes[key].split(',').map(&:to_i)
      end
    end
    

    The problem currently lies in (friend_params.dig(:friend_attributes) || []).each which loops though the key-value pairs, or if not present through an empty array. You then call the #[] method on the value (which is a string). eg. "50589,50590"['players'] tries the find the substring 'players' inside "50589,50590" the result of this should be nil. Followed by an "NoMethodError: undefined method `split' for nil:NilClass" exception.

    You should simply use the returned collection, rather than looping through the key-value pairs.


    The better question is, why do you need to transform your parameters at all?

    Can't the view be arranged in such a way to provide the parameters in array format?

    <input name="user[friend_attributes][players][]" type="checkbox" value="1" />
    <input name="user[friend_attributes][players][]" type="checkbox" value="2" />
    <input name="user[friend_attributes][players][]" type="checkbox" value="3" />
    

    Would send an array of numbers to the controller to start with, eliminating the need to convert parameters into the correct format.

    Note: If you supply the parameters in the above format you should update your permited params to:

    params.require(:user).permit(:first_name, :last_name, friend_attributes: [:phone_number, :title, :role, players: [], leaders: []])
    # players and leaders are now expecting an array instead of a scalar value