ruby on rails - Refactor code to avoid circular logic in controller -
situation:
- one form allows users select multiple quantities of items they'd request
- this form posts 2 models, 1 parent: request, , child: items.
- upon submit, 1 request created, several items created, depending on quantity indicated
- to handle this, have 2 sets of params, 1 items, 1 requests
desired end state:
- i not want item created without request nor request created without item
- all errors present in form (whether it's not selecting @ least 1 item, or errors in attributes of request object) shown user once page re-rendered; i.e., error checking together
current hacky solution & complication:
- currently, i'm checking in stages, 1) there quantities in items? if not, regardless of user may have put request attributes, page re-rendered (i.e., attributes request lost, validation errors shown). 2) once first stage passed, model validations kicks in, , if fails, new page re-rendered again
i've spent waaaaay long thinking this, , nothing elegant comes mind. happy hacky solution, love insights smarter people!
controller code (fat now, fix later)
def create request_params @requestrecord = @signup_parent.requests.build if @itemparams.blank? @requestrecord.errors[:base] = "please select @ least 1 item" render 'new' else @requestrecord = @signup_parent.requests.create(@requestparams) if @requestrecord.save items_to_be_saved = [] @itemparams.each |item, quantity| quantity = quantity.to_i quantity.times items_to_be_saved << ({:request_id => 0, :name => item }) end end item.create items_to_be_saved flash[:success] = "thanks!" redirect_to action: 'success' else render 'new' end end end def request_params @requestparams = params.require(:request).permit(:detail, :startdate, :enddate) @itemparams = params["item"] @itemparams = @transactionparams.first.reject { |k, v| (v == "0") || (v == "")} end
and in case it's helpful, snippet of view code generates params["item"]
<% itemlist.each |thing| %> <%= number_field_tag "item[][#{thing}]", :quantity, min: 0, placeholder: 0 %> <%= label_tag thing %> </br> <% end %> <!-- itemlist variable in controller populated list of items -->
validations
when mention want errors returned @ same time, means need use rails' validations functionality.
this populates @model.errors
object, can use on form
this:
<% if @model.errors.any? %> <ul> <% @model.errors.full_messages.each |msg| %> <li><%= msg %></li>
i think problem you're trying use validations in controller. both against mvc principles & bad programming modularity. functionality require available validations
features:
you may benefit using inverse_of
create conditional validations; or using reject_if
reject_if
#app/models/request.rb class request < activerecord::base accepts_nested_attributes_for :items, reject_if: proc { |attributes| attributes['an_item_param'].blank? #-> attributes "item" attributes } end
this triggered if request created. i.e if request fails reason (validation issue), accepts_nested_attributes_for
method not run, returning object appended errors
this used validate nested resources (i.e can't save item
unless title
attribute populated etc)
--
inverse_of
#app/models/request.rb class request < activerecord::base has_many :items, inverse_of: :request accepts_nested_attributes_for :items end #app/models/item.rb class item < activerecord::base belongs_to :request, inverse_of: :items validates :title, presence: true, unless: :draft? private def draft? self.request.draft #-> example we've used before :) end end
this more model-specific validations; allowing determine specific conditions. use if want save draft etc
Comments
Post a Comment