user12855055
user12855055

Reputation:

Discounts the whole products belongs to category?

I have these tables:

products
-- name
-- price
-- quantity
-- category_id

discounts
-- type('percentage','numeric')
-- value
-- expired_at

categories 
-- name

discountables
-- discount_id
-- discountable_id
-- discountable_type

In discountables there is many to many relationship between:

discounts and categories also discounts and products

I'm done with how to discount between discounts and products

Now I'm confused How to discount the whole products that belongs to category that I add todiscountables

Relation:

Category Models

public function discounts()
{
    return $this->morphToMany('App\Models\Discount', 'discountable');
}

Products Models

public function discounts()
{
    return $this->morphToMany('App\Models\Discount', 'discountable');
}

Discount Model:

public function categories()
{
    return $this->morphedByMany('App\Models\Category', 'discountable')->withTimestamps();
}

public function products()
{
    return $this->morphedByMany('App\Models\Product', 'discountable')->withTimestamps();
}

MY code for discount the products directly discounts and products

  /**
 * get price of product after discount
 *
 * @return void
 */
public function getDiscountProductAttribute() {
    foreach ($this->discounts as $discount) {
        if($discount->expired_at > Carbon::now()){
            if ($discount->type == 'numeric'){
                return $this->price - $discount->value;
            }else{
                return $this->price - ($this->price * ($discount->value / 100));
            }
        }
    }
}

So I need How to discount the whole products that belongs to category that I add todiscountables?

Upvotes: 2

Views: 618

Answers (1)

Kevin Bui
Kevin Bui

Reputation: 3035

Your relationships are fine. I just redesign a bit to solve the problem.

class Discount extends Model
{
    /**
    * Check whether this discount is expired.
    *
    * return bool
    */
    public function expired()
    {
        // I assume that expired_at is also a Carbon instance.
        return $this->expired_at->isPast();
    }

    /**
    * Return the discount amount for each product.
    *
    * @return double
    */
    public function apply(Product $product)
    {
        if ($this->type === 'numeric') {
            $this->value;
        }

        if ($this->type === 'percentage') {
            return $product->price * ($this->value / 100);
        }

        return 0;
    }
}
class Product extends Model
{
    public function allDiscounts()
    {
        return $this->discounts
            ->merge($this->category->discounts)
            ->unique();
    }

    pubic function getTotalDiscountAttribute()
    {
        return $this->allDiscounts()
            ->reject
            ->expired()
            ->map
            ->apply($this)
            ->sum();
    }

    public function getTotalPriceAttribute()
    {
         return $this->price - $this->total_discount;
    }
}

So you can get the total price for a single product after applying all sort of discounts from itself and its category (if there's any) by simply saying:

$product->total_price;

I hope that helps. Let me know if it fails at any step.

By the way. Your question reminds me of a pretty good speech a few years back. Its about solving a some what similar problem using inheritance and Null Object Pattern. It's pretty cool. You don't need to rewrite it that way but its a pretty good design.

Upvotes: 0

Related Questions