3

I'm looking to implement some e-commerce functionality that gives discounts when certain quantities are reached. The catch is, its not quantities of one sku, any number of other products in a category can trigger the quantity break when in total they reach the threshold.

So if I have a model class for a Cart_Product lets say, I would typically put the logic for getting the prices in that class as a method. But since other instances of that class in the current cart need to be considered, I'm not sure of the best way to proceed.

Do I call the "owner" Cart instance inside of the Cart_Product get_price method and then add the logic to check for the quantity break? Or is there a better design pattern to use at this step?

tereško
  • 58,060
  • 25
  • 98
  • 150
cactusphone
  • 507
  • 4
  • 15

3 Answers3

1

First of all, model is not a class or instance. Model is a layer. What you are talking about in your question actually are domain objects (assuming they are not also responsible for saving themselves, which would violate SRP.

As for applying the discount, it depends on whether each product in your cart has a separate discount of the discount is same for all the products:

  • if each product can have a separate discount, then the logic for that should reside in the Product domain object.

  • if all products get the same discount, then the discount should affect only the sum total, therefore - compute in the Cart instance.

Community
  • 1
  • 1
tereško
  • 58,060
  • 25
  • 98
  • 150
1

The logic you have described is a cart-wide feature; since the cart is the logical owner of the products inside, you would implement it there:

class Cart
{
    private $products; // Cart_Product[]

    // ...

    function calculateDiscount()
    {
        $totalQuantity = array_reduce($this->products, function($sum, $product) {
            return $sum + $product->getQuantity();
        }, 0);

        if ($totalQuantity > 10) {
            $this->cartDiscount = 25; // apply 25% discount on the cart
        } else {
            $this->cartDiscount = 0;
        }
    }
}

This introduces a separate entity for a global cart discount. If you don't want that, you would have to apply the discount to each individual item.

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
0

i just went through something very similar. really the only thing the cart should know about is the product id and the quantity. everything else should be for display purposes only. in other words the product object is always responsible for the price. the only reason that a price is stored in the cart is to help show it in the view. otherwise we assume that the price always has to be checked with any insert or update, to prevent fraud.

here is another scenario - you have a special on shipping, like buy $100 worth of qualifying goods and you get free shipping. there might be a separate shipping special on specific products. the only way to calculate is with all of the cart items.

so my solution - which i am not sure is optimal - is to pass the cart items to a shipping object - do the shipping calculations - optionally add messaging for specific products to display in the cart - and then return the cart items.

otherwise you are having to put shipping methods in the cart class which does not make any sense but maybe there is another way to do this.

here is another scenario - inventory control. someone orders 30 blue widgets but you only have 10 blue widgets. ok you can check for inventory when you insert item in cart. but what if they update the cart and then increase to 30? that means that we have to check inventory - for every item in the cart - every time the cart is updated. and if we are doing that then might as well get the price in case it has gone up or down.

so i take the cart items - and pass them to a product object - which checks inventory - and if necessary reduces the quantity of the items down to current inventory - optionally adds messaging explaining that stock is limited - then passes back to cart object.

finally - suggest that you have an object that owns the shopping session. and then thats where the totals would be. that way the cart is never in charge of totals - its just a container. one way is you just start an order and then store the different totals there.

cartalot
  • 3,147
  • 1
  • 16
  • 14