Skip to content

一个 灵活可扩展 的 PHP 促销策略引擎,让购物车的各种复杂促销逻辑(满减、打折、阶梯优惠、会员折扣…)实现更优雅、可维护 | A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

License

Notifications You must be signed in to change notification settings

zxc7563598/php-promotion-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🛒 hejunjie/promotion-engine

A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

This project has been parsed by Zread. If you need a quick overview of the project, you can click here to view it:Understand this project


📦 Installation method

Install this library using Composer:

composer require hejunjie/promotion-engine

🚀 Usage

use Hejunjie\PromotionEngine\Models\Cart;
use Hejunjie\PromotionEngine\Models\User;
use Hejunjie\PromotionEngine\PromotionEngine;
use Hejunjie\PromotionEngine\Rules;

// Create user (VIP)
$user = new User(vip: true);

// Create a shopping cart (with 10 items, and the total price can cover various rules)
$cart = new Cart();
$cart->addItem('商品A', 50, 1, ['snacks']);
$cart->addItem('商品B', 60, 1, ['clothes']);
$cart->addItem('商品C', 40, 1, ['clothes']);
$cart->addItem('商品D', 100, 1, ['promo']);
$cart->addItem('商品E', 30, 3, ['snacks']);
$cart->addItem('商品F', 200, 1, ['electronics']);

// Initialize the promotion engine
$engine = new PromotionEngine();
// Explanation of promotion calculation mode:
//
// independent (independent mode):
// Each rule calculates the discount amount separately based on the "original price of the product",
// Finally, the discounts will be consolidated, and the total price will not be affected by intermediate calculations from other rules.
//
// sequential (discount-on-discount mode):
// Each rule continues to calculate based on the "previously calculated price", resulting in a "discount on discount" effect.
// When a rule involves multiple products, the discount amount will be allocated based on the proportion of each product's original price in the total combined price,
// To ensure that the price of each item is updated correctly in subsequent calculations.
//
// lock (locked mode):
// Each item will only be discounted by one rule at most, and subsequent rules will not apply repeated discounts to items that have already been locked.
// Applicable to business scenarios where "a certain product can only enjoy one discount" (such as flash sales, exclusive coupons, etc.).
$engine->setMode('independent');

// Register all rules
$engine->addRule(new Rules\FullDiscountRule(200, 0.9));  // 10% off for purchases over 200
$engine->addRule(new Rules\FullQuantityReductionRule(3, 20));  // Buy 3 or more and get 20% off
$engine->addRule(new Rules\NthItemDiscountRule(3, 0.5));  // Third item is 50% off
$engine->addRule(new Rules\VipDiscountRule(0.95));  // 5% discount for VIP
$engine->addRule(new Rules\FullQuantityDiscountRule(5, 0.9));  // 10% off for purchases of 5 items or more
$engine->addRule(new Rules\FullReductionRule(100, 20));  // 20 off for purchases over 100
$engine->addRule(new Rules\VipReductionRule(5));  // VIP: 5 yuan off

// Perform calculation
$result = $engine->calculate($cart, $user);

// Print result (for manual verification)
echo "\n=== test result ===\n";
echo "original price: ¥{$result['original']}\n";
echo "discount: -¥{$result['discount']}\n";
echo "final: ¥{$result['final']}\n";
echo "Discount Details:\n";
foreach ($result['details'] as $detail) {
    echo "- {$detail}\n";
}

// 输出内容:
// === test result ===
// original price: ¥540
// discount: -¥200
// final: ¥340
// Discount Details:
// - 指定商品满200元打0.9折 (-¥54)
// - 指定商品满3件减20 (-¥20)
// - 指定商品第3件打5折 (-¥20)
// - VIP 0.95 折 (-¥27)
// - 指定商品满5件打9折 (-¥54)
// - 指定商品满100减20 (-¥20)
// - VIP 立减 5 元 (-¥5)

🛠️ Rule type (out-of-box)

方法 说明
FullDiscountRule For purchases over X yuan, receive a Z% discount (Example: For purchases over 200 yuan, receive a 20% discount)
FullQuantityReductionRule Buy X items and get Y yuan off (Example: Buy 3 items and get 20 yuan off)
NthItemDiscountRule The Nth item is discounted by Z (Example: the 3rd item is discounted by 50%)
TieredDiscountRule Enjoy a Z% discount when your purchase reaches X yuan (Example: 10% discount when your purchase reaches 100 yuan, 20% discount when your purchase reaches 200 yuan, 30% discount when your purchase reaches 500 yuan (whichever is the highest))
VipDiscountRule VIP users enjoy discounts (Example: 10% off for VIP)
FullQuantityDiscountRule Buy X items and get Z% off (Example: Buy 3 items and get 10% off)
FullReductionRule For purchases over X yuan, get Y yuan off (Example: for purchases over 100 yuan, get 20 yuan off)"
NthItemReductionRule The Nth item is on special offer (Example: the 3rd item is priced at 9.9 yuan)
TieredReductionRule Step discount: subtract Y yuan when the purchase reaches X yuan (Example: subtract 10 yuan when the purchase reaches 100 yuan, subtract 30 yuan when the purchase reaches 200 yuan, and subtract 80 yuan when the purchase reaches 500 yuan (whichever is the highest level))
VipReductionRule VIP users enjoy immediate discounts (Example: a discount of 5 yuan when ordering as a VIP)

🎯 Purpose & Original Intent

Why write this package?

In e-commerce, B2C, and retail systems, promotion logic is often scattered across Controller, Service, and if-else statements. Once there are numerous rules, maintenance becomes a nightmare.

✅ I hope to solve this problem using a 'rule engine + extensible policy class':

  • Write each discount as a separate class → clear, easy to test, reusable
  • Supports multiple calculation modesoriginal (based on original price) / stack (discount on discount)
  • Easy to extend → Adding new rules only requires extends PromotionRule

👉 In this way, for future integrations of new activities / membership levels / specific product category discounts, it will only be necessary to create a new Rule, without the need for major code modifications.


🤝 Welcome PR & contributions

📢 All pull requests (PRs) are welcome:

  • New promotional rules (such as coupons, buy-one-get-one-free, flash sales, etc.)
  • Optimize core logic (such as more flexible rule priorities, discount stacking strategies)
  • Unit testing (PHPUnit) or performance optimization

👉 Fork this repository and submit a Pull Request, or create an issue to exchange ideas.

About

一个 灵活可扩展 的 PHP 促销策略引擎,让购物车的各种复杂促销逻辑(满减、打折、阶梯优惠、会员折扣…)实现更优雅、可维护 | A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages