Reputation: 4511
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
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