PHPackages                             zgldh/discount-and-coupon - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. zgldh/discount-and-coupon

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

zgldh/discount-and-coupon
=========================

The core logic about discount and coupon. Useful to manage discount campaign, defining coupon and calculate the final price.

v1.1.1(7y ago)10641MITPHPPHP &gt;=7.0.0

Since Sep 23Pushed 7y ago2 watchersCompare

[ Source](https://github.com/zgldh/discount-and-coupon)[ Packagist](https://packagist.org/packages/zgldh/discount-and-coupon)[ RSS](/packages/zgldh-discount-and-coupon/feed)WikiDiscussions master Synced 2w ago

READMEChangelog (10)Dependencies (1)Versions (11)Used By (0)

zgldh/discount-and-coupon
=========================

[](#zgldhdiscount-and-coupon)

优惠活动、折扣计算器

[![Build Status](https://camo.githubusercontent.com/cc9a4ed77e58f814b832740591e0d0cf3b688bed838a3f9b7f7862c36d49c9a9/68747470733a2f2f7472617669732d63692e636f6d2f7a676c64682f646973636f756e742d616e642d636f75706f6e2e7376673f6272616e63683d6d6173746572)](https://travis-ci.com/zgldh/discount-and-coupon)

定义
--

[](#定义)

1. 折扣活动，通常用于店铺对所有消费者的活动。
2. 优惠券，通常属于一个特定的消费者的特权。
3. 以上两者在下文统称“benefit 权益”
4. `zgldh\DiscountAndCoupon\Benefit::class` 是可享用的权益的基类，用于定义一系列折扣活动、优惠券的类型。注意是定义类型而不是实例。
5. `priority` 表示该权益的判断优先级，数值越高越优先判断。

功能
--

[](#功能)

1. 输入“权益(benefit)”和一些货品，即可计算出最终价格。
2. 可以得出每一个货品参与了哪些 权益。

用法
--

[](#用法)

先给个感性认识。

```
    use zgldh\DiscountAndCoupon\Calculator;

    $calculator = new Calculator();         // 初始化计算器
    $result = $calculator
        ->setBenefits($discountCollection)  // 设置打算应用的 Benefit  可选 Optional
        ->setProducts([                      // 设置要买的货物            必填
            [
                'sku'       => 123,         // 货物 SKU 或 ID   必填
                'price'     => 2.80         // 货物原价         必填
                'category'  => 456,         // 货物分类 ID      可选。如不填写，无法参与针对分类的活动。
                'name'      => 'Coke Cola'  // 货物名           可选
                'foo'       => 'bar'        // 其他参数         可选
            ],
            ... // more products
         ])
        ->calculate();                      // 开始计算

    $result->getPrice();                    // 原始总价， 两位小数
    $result->getFinalPrice();               // 最终总价， 两位小数
    $result->getProfit();                   // final_price 减去 price
    $result->getBenefits();                 // 实际应用的 benefits， 内含每个 benefit 提供了多少 profit。

    foreach($result->getProducts() as $product) // 最终的货物集合，每个元素对应一个货物
    {
        $product->getSku();                     // 该货物 SKU
        $product->getPrice();                   // 货物原价
        $product->getFinalPrice();              // 货物均摊最终价 （仅供参考）
        $product->getCategory();                // 货物分类 ID
        $product->getName();                    // 货物名字
        $product->foo;                          // 其他参数
        $product->getBenefits();                // 该货物应用的 benefits
    }
```

### 如何定义店铺折扣活动 Discount

[](#如何定义店铺折扣活动-discount)

#### 1. 定义店铺的“满减活动”：

[](#1-定义店铺的满减活动)

```
use zgldh\DiscountAndCoupon\Benefit;

class FlatDiscountWhenPurchaseExceed extends Benefit{
    private $above = null;      // 满多少钱
    private $deduction = null;  // 减多少钱

    protected $priority = 100;    // 默认优先级

    protected function isScopeQualified($scopeProducts, $scopeTotalPrice)
    {
        return $scopeTotalPrice >= $this->above;
    }

    protected function newScopePrice($scopeProducts, $scopeTotalPrice)
    {
        return $scopeTotalPrice - $this->deduction;
    }
}

// 满20减2
$d2a20 = new FlatDiscountWhenPurchaseExceed(['above'=>20,'deduction'=>2,'priority'=>101]);
// 满50减10
$d10a50 = new FlatDiscountWhenPurchaseExceed(['above'=>50,'deduction'=>10,'priority'=>102]);
// 满100减30
$d30a100 = new FlatDiscountWhenPurchaseExceed(['above'=>100,'deduction'=>30,'priority'=>103]);
```

上面我们定义了 `FlatDiscountWhenPurchaseExceed` 类，用于描述“满减”这一类活动。

然后我们新建了 3 个该类的对象，分别代表满20减2, 满50减10, 满100减30 这三种活动。

注意 `priority` 属性， 它决定了 `Calculator` 在尝试应用折扣时的判断顺序。由于默认以上三种活动同属于“满减”活动，且只能应用其中一种(请参考 `Benefit` 类的源码中对 `$group` 的说明)。所以我们让优惠额度最大的优先级最高，以便总是能帮客户享用最大的优惠力度。

#### 2. 定义店铺的“迎中秋早餐8折”活动，不与其他活动同享：

[](#2-定义店铺的迎中秋早餐8折活动不与其他活动同享)

```
use zgldh\DiscountAndCoupon\Benefit;

const CATEGORY_BREAKFAST = 'breakfast';

class MidAutumnDayBreakfast80Discount extends Benefit{
    protected $priority = 200;    // 优先级

    protected $exclusive = true;  // 不与其他活动同享

    protected function scope(Product $product){
        return $product->category === CATEGORY_BREAKFAST;
    }

    protected function newScopePrice($scopeProducts, $scopeTotalPrice)
    {
        return $scopeTotalPrice * 0.8;
    }
}

$midAutumnDayDiscountEvent = new MidAutumnDayBreakfast80Discount();
```

#### 3. 定义“新品促销，XX酸奶买一送一” 活动，不与其他活动同享。

[](#3-定义新品促销xx酸奶买一送一-活动不与其他活动同享)

下列代码是以减价实现本促销活动，会影响本次订单最终价格。

```
use zgldh\DiscountAndCoupon\Benefit;

const SKU_YOGURT = 'yogurt';

class YogurtBuyOneGetOne extends Benefit{
    protected $priority = 500;    // 优先级

    protected $exclusive = true;  // 不与其他活动同享

    protected function scope(Product $product){
        return $product->sku === SKU_YOGURT;
    }

    protected function newScopePrice($scopeProducts, $scopeTotalPrice)
    {
        $productsCount = count($scopeProducts);
        $fairCount = ceil($productsCount / 2);
        $fairProductPrice = $scopeTotalPrice / $productsCount;
        return $fairProductPrice * $fairCount;
    }
}

$yogurtPromotion = new YogurtBuyOneGetOne();
```

下列代码是以赠送货品实现促销活动，不会影响本次订单最终价格，但会增加商品。

```
use zgldh\DiscountAndCoupon\Benefit;
use zgldh\DiscountAndCoupon\Product;

const SKU_YOGURT = 'yogurt';

class YogurtBuyOneGetOne extends Benefit{
    protected $priority = 500;    // 优先级

    protected $exclusive = true;  // 不与其他活动同享

    protected function scope(Product $product){
        return $product->sku === SKU_YOGURT;
    }

    protected function newScopeProducts($scopeProducts, $scopeTotalPrice)
    {
        $count = count($scopeProducts);
        for($i = 0; $i SKU_YOGURT,
                'price'=>0
            ]));
        }
        return $scopeProducts;
    }
}

$yogurtPromotion = new YogurtBuyOneGetOne();
```

### 如何定义优惠券 Coupon

[](#如何定义优惠券-coupon)

其实优惠券和折扣活动逻辑上没有区别，请在您具体的业务代码里，删掉使用过的优惠券。

#### 1. 定义代金券

[](#1-定义代金券)

```
use zgldh\DiscountAndCoupon\Benefit;

class FlatDeduction extends Benefit{
    protected $priority = 0;    // 优先级
    protected $deduction = 0;
    protected $couponId = null;
    protected $groupMaxApplyTime = 2; // 最多可以同时用两张代金券

    protected function newScopePrice($scopeProducts, $scopeTotalPrice)
    {
        return max(0, $scopeTotalPrice - $this->deduction);
    }
}

$flatDeduction10= new FlatDeduction([ 'deduction'=>10, 'coupon_id'=>123 ]); // 10元代金券
$flatDeduction20= new FlatDeduction([ 'deduction'=>20, 'coupon_id'=>124 ]); // 20元代金券
$flatDeduction50= new FlatDeduction([ 'deduction'=>50, 'coupon_id'=>125 ]); // 50元代金券
```

#### 2. 定义 “饮料升级优惠券”

[](#2-定义-饮料升级优惠券)

能把订单内的某种饮料的一件商品替换成更贵的饮料。 本质是替换商品，所以你可以任意定义规则。

```
use zgldh\DiscountAndCoupon\Benefit;

const SMALL_COKE = 'small-coke';
const BIG_COKE = 'big-coke';

class UpgradeBeverage extends Benefit{
    protected $priority = 500;    // 优先级

    protected function scope(Product $product){
        return $product->sku === SMALL_COKE;
    }

    protected function newScopeProducts($scopeProducts, $scopeTotalPrice)
    {
        $firstCoke = $scopeProducts[0];
        if($firstCoke)
        {
            $firstCoke['sku'] = BIG_COKE;
            $firstCoke['name'] = 'Big Coke';
        }
        return $scopeProducts;
    }
}
```

###  Health Score

31

—

LowBetter than 66% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity19

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity63

Established project with proven stability

 Bus Factor1

Top contributor holds 85% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~20 days

Recently: every ~28 days

Total

10

Last Release

2655d ago

Major Versions

v0.1.0 → v1.0.02018-09-27

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/312404?v=4)[zgldh](/maintainers/zgldh)[@zgldh](https://github.com/zgldh)

---

Top Contributors

[![zgldh](https://avatars.githubusercontent.com/u/312404?v=4)](https://github.com/zgldh "zgldh (17 commits)")[![gitliyu](https://avatars.githubusercontent.com/u/29886054?v=4)](https://github.com/gitliyu "gitliyu (3 commits)")

---

Tags

coupondiscount

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/zgldh-discount-and-coupon/health.svg)

```
[![Health](https://phpackages.com/badges/zgldh-discount-and-coupon/health.svg)](https://phpackages.com/packages/zgldh-discount-and-coupon)
```

###  Alternatives

[frittenkeez/laravel-vouchers

Voucher system for Laravel 11+

5721.2k2](/packages/frittenkeez-laravel-vouchers)[atelierdisko/coupon_code

PHP library to generate and validate coupon code strings.

60195.8k2](/packages/atelierdisko-coupon-code)[magepsycho/magento2-discountlimit

Magento 2 Discount Amount Limiter

1012.1k](/packages/magepsycho-magento2-discountlimit)[smile/module-multi-coupon

Extension of Magento2 Sales Rule to allow multiple coupon usage.

102.9k](/packages/smile-module-multi-coupon)[blesta/pricing

A library for handling pricing and pricing modifiers

131.1k](/packages/blesta-pricing)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
