edhel
edhel

Reputation: 203

Rails - add validation errors from controller

Booking model belongs_to DiscountCode. The booking form has a string input for discount_code, which is turned to DiscountCode in the controller:

updated_params = booking_params
if updated_params[:discount_code]
  updated_params[:discount_code] = DiscountCode.find_by code: updated_params[:discount_code]
end
@booking = Booking.new(updated_params)

So right now if the code entered does not exist, updated_params[:discount_code] is nil, which is the same as if no discount code was entered. I would rather add an error to @booking.discount_code in that if statement, but I'm in the controller, is that possible?

Upvotes: 0

Views: 1758

Answers (1)

Max Williams
Max Williams

Reputation: 32933

The solution to this sort of thing is generally to do something very simple, standard, agnostic and scaffoldy with @booking in the controller, like @booking = Booking.new(params[:booking]) or @booking.update_attributes(params[:booking]). Always try to keep your controller methods as standard as possible and push logic into the models and data into the views.

So, that leads us on to passing the discount code through via a Booking setter method. If we don't have that setter method already from Rails, then we need to add it. Let's assume that there's already an association like "has_one :discount_code" in Booking. We can write a setter method to make this association from the code, and validate it like so.

(note that it's confusing to have a field called "code" in a class called "DiscountCode" - you're going to be writing @discount_code.code which begs the question "is a 'code' a string of letters and numbers like '12dafj1213', or is a 'code' an ActiveRecord object loaded out of the database?". I'd recommend either renaming the class to "Discount" (so you would say @discount.code) or renaming the "code" field to "token" (so you would say @discount_code.token). I haven't addressed this issue in my solution, i've stuck with your naming, so my code looks quite confusing with all the "code_code" stuff.

class Booking
  attr_accessor :discount_code_code
  has_one :discount_code

  before_validation :set_discount_code_from_discount_code_code
  validate :check_we_have_discount_code_if_we_have_discount_code_code

  def set_discount_code_from_discount_code_code
    unless self.discount_code_code.blank?
      self.discount_code = DiscountCode.find_by_code(self.discount_code_code)
    end
  end

  def check_we_have_discount_code_if_we_have_discount_code_code
    if !self.discount_code_code.blank? && !self.discount_code
      self.errors.add(:discount_code, "is not a valid discount code")
    end    
  end
end

Your form (which i'd expect to be a form_for @booking) should include something like this

<%= f.text_field :discount_code_code %>

which will come through in params like

params = {:booking => {:discount_code_code => "12dafj1213"}}

and then in the controller you should just say

@booking = Booking.new(params[:booking])
if @booking.save
  ...etc

Upvotes: 4

Related Questions