vorobey
vorobey

Reputation: 4511

Rails 3 special validation

i met difficult situation.

class Cart < ActiveRecord::Base
  has_many :cart_items
  accepts_nested_attributes_for :cart_items
end

class CartItem << ActiveRecord::Base
  belongs_to :cart
  belongs_to :item

  def for_friend?
    true if self.friend_email?
  end
end

Class Item << ActiveRecord::Base
  MAXIMUM_ITEMS_PER_USER = 10

  has_many :cart_items
end

Okay, this is simple association case. Each cart can has many cart_items. But we can't sold more then 10 items for one user (another words, 1 user can buy only 10 GREEN T-SHIRT). This limitation defined by Item::MAXIMUM_ITEMS_PER_USER.

The simplest case of validation is per record validation:

for example: we have CartItem object:

CartItem id: 1, cart_id: 1, item_id: 1, count: 1, friend_email: nil

and some validator defined in CartItem model:

class CartItem < ActiveRecord::Base
  validates :count_per_user
end

def count_per_user
  self.errors.add(:base, "ONLY 10 WE CAN") if self.count > Item::MAXIMUM_ITEMS_PER_USER
end

Okay, if someone set CartItem object count to more than 10 we will raise error.

But in my case user can buy some item for friend. So user can have to CartItems in hes Cart (difference will be only in COUNT and friend_email fields).

It looks like this:

CartItem id: 1, cart_id: 1, item_id: 1, count: 1, friend_email: nil

CartItem id: 2, cart_id: 1, item_id: 1, count: 2, friend_email: [email protected]

The problem is what we need to validate sum of "count fields" of two records :)

the methods what i find is to place validation in controller:

# controller method #update
def update
  @cart = Cart.find(params[:id])
  update_and_validate_count(params)
end

def update_and_validate_count(controller_params)
  @cart_items = get_and_update_cart_items_from(controller_params) # just assign new values, but doesn't save any
  summ = @cart_items.inject(0) {|result,r| result += r.count}
  if summ > Item::MAXIMUM_ITEMS_PER_USER
    @cart.errors.add(:base, "WRONG COUNT, ONLY 10 IS POSSIBLE")
  else
    @cart.update_attributes(controller_params[:cart])
  end
end

This is nice methods to solve this problem, but this is looks bad ( Can you give me some advice? Or maybe u have some exp in this case?

Upvotes: 0

Views: 1114

Answers (1)

tadman
tadman

Reputation: 211560

You should be able to perform this validation on the Cart level, not the CartItem. Something like this:

def validate_count_requirements
  if (self.cart.cart_items.sum(:count) > Item::MAXIMUM_ITEMS_PER_USER)
    self.errors.add(:base, "ONLY #{Item::MAXIMUM_ITEMS_PER_USER} WE CAN")
  end
end

While this isn't super efficient, it should work in most cases. You can even scope the cart_items as you need to perform a SUM() operation only on certain values.

As a note, count is a bad name for an attribute because it is a reserved SQL keyword, though it will work because ActiveRecord escapes it properly. If you forget to do this in any of your custom queries you may have to pay special attention to it. A good non-conflicting alternative is quantity.

Upvotes: 4

Related Questions