Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails STI and strong parameters

I have a Client resource with 2 types: Person and Company.

routes.rb:

resources :clients
resources :people, :controller => "clients", :type => "Person"
resources :companies, :controller => "clients", :type => "Company"

clients_controller:

def new
  @client = Client.new()
  @client.type = params[:type]
end

def create
  @client = current_partner.clients.new(client_params)
  if @client.save
    redirect_to clients_path
  ...
end

...
private
def client_params
  params.require(:client).permit(:type, :partner_id, :name, :email, :phone, :cui,   :registration_id, :address)
end

def find_client
  @client ||= Client.find(params[:id])
end

client.rb

class Client < ActiveRecord::Base
  validates_presence_of :type
  CLIENT_TYPES = ['Person', 'Company']
end

person.rb

class Person < Client
  validates_presence_of :name, :email, :phone
end

compay.rb

class Company < Client
  validates_presence_of :name, :email, :cui, :registration_id, :phone
  validates_uniqueness_of :cui, :registration_id, uniqueness: {scope: :partner_id}
end

The problem is when I'm trying to edit a client's details and I submit the changes, I get param is missing or the value is empty: client. The route from where I'm getting this error is .../companies/3.

Any help on this noobie question? Thanks!

like image 518
Bogdan Popa Avatar asked Aug 13 '14 08:08

Bogdan Popa


2 Answers

You can disguise your inherited model to it's parent before the permit action by

def client_params

  if params.has_key? :person
    params[:client] = params.delete :person
  elsif params.has_key? :company
    params[:client] = params.delete :company
  end

  params.require(:client).permit(...)

end

Its just renaming the model's key in the params hash, and does the trick.

like image 64
ran632 Avatar answered Sep 26 '22 12:09

ran632


Models

I think you're not using STI's properly

STI's are for Models, not controllers. As per the MVC programming pattern, your Models handle all the data-construction methodology. Your controllers are used as an interim between your user inputs & your views:

enter image description here

This means that if you want to use or create STI-driven functionality, you'll be best just using the backend classes for it (instead of manually passing type etc):

#app/models/person.rb
Class Person < Client
   ...
end

#app/models/company.rb
Class Company < Client
end

#app/models/client.rb
Class Client < ActiveRecord::Base
end

This will give you the ability to do this:

#config/routes.rb
resources :clients
resources :people,    controller: "clients", type: "Person"
resources :companies, controller: "clients", type: "Company"

#app/controllers/clients_controller.rb
Class ClientsController < ApplicationController
    def create
       model = get_model(params[:type])

       @model = model.new(model_params)
       @model.save
    end

    private

    def get_model type
       return type.singularize.titleize.camelize.constantize
    end

    def model_params
        params.require(params[:type].to_sym).permit(:client, :model, :attributes)
    end
end

This will the various STI Model that you've set by accessing /people or /companies, and then give you the ability to save data to it (which will save to the Client model with type set, of course)

like image 32
Richard Peck Avatar answered Sep 26 '22 12:09

Richard Peck