Installation
composer require php-junior/pricing-engine
php artisan vendor:publish --provider="PhpJunior\PricingEngine\Providers\PricingEngineServiceProvider"
php artisan migrate
config/pricing-engine.php is published and updated if needed (e.g., model/table names).First Use Case: Apply a Discount
use PhpJunior\PricingEngine\Facades\PricingEngine;
$finalPrice = PricingEngine::make()->applyRules(
basePrice: 100.00,
context: ['user_id' => 1, 'product_id' => 101]
);
user_id, product_id, cart_total) to evaluate rules.Defining Rules via Facade
PricingEngine::make()->savePricingRule(
name: 'Black Friday 20%',
priority: 10,
conditions: [
new ConditionData('date', 'between', ['2025-11-20', '2025-11-25']),
new ConditionData('user.role', 'in', ['premium', 'vip'])
],
actions: [
new ActionData('discount', 20) // 20% discount
]
);
config/pricing-engine.php).Dynamic Context Evaluation
$context = [
'user' => auth()->user(),
'cart' => $cart->load('items'),
'date' => now()->format('Y-m-d')
];
$price = PricingEngine::make()->applyRules(99.99, $context);
user.role) to access nested objects/arrays.Rule Groups (Composite Rules)
PricingEngine::make()->savePricingRuleGroup(
name: 'Seasonal Promotions',
rules: [1, 2, 3] // IDs of pre-defined rules
);
applyRuleGroup($groupId, $context).Testing Rules
$rule = PricingEngine::make()->getRule(1);
$isApplicable = $rule->evaluate($context);
OrderCreated or CartUpdated events to re-evaluate pricing dynamically.
event(new OrderCreated($order));
// Inside listener:
$order->final_price = PricingEngine::make()->applyRules($order->base_price, $order->context);
data.price_after_rules).// Example Nova Tool
Nova::tools([
new PricingRuleManager,
]);
Context Mismatches
user_id vs. user.id).dd($context) to verify keys before applying rules.Priority Conflicts
priority: 1 for VIP, priority: 10 for seasonal).Circular Dependencies
applyRules() with a maxIterations parameter (if supported) or refactor logic.Database Locks
savePricingRule().PricingEngine::make()->queueRuleUpdate($ruleData);
Log Evaluation Steps Enable debug mode in config:
'debug' => env('PRICING_ENGINE_DEBUG', false),
Logs will show which rules were evaluated and why they passed/failed.
Rule Validation
Use the validateRule() method to check syntax before saving:
$errors = PricingEngine::make()->validateRule($ruleData);
if ($errors) throw new \Exception($errors);
Custom Operators/Actions
Extend the package by registering new operators (e.g., contains) or actions (e.g., free_shipping):
// config/pricing-engine.php
'operators' => [
'contains' => \PhpJunior\PricingEngine\Operators\ContainsOperator::class,
],
'actions' => [
'free_shipping' => \App\Actions\FreeShippingAction::class,
],
Model Binding Bind custom models for rules/conditions:
// config/pricing-engine.php
'models' => [
'rule' => \App\Models\CustomPricingRule::class,
],
Caching Strategies Cache evaluated rules for performance:
Cache::remember("pricing_rules_{$contextHash}", now()->addHours(1), function() {
return PricingEngine::make()->getAllRules();
});
Localization
Support multi-language rule names/descriptions by extending the Rule model:
use Spatie\Translatable\HasTranslations;
class CustomPricingRule extends Model implements HasTranslations {
public $translatable = ['name', 'description'];
}
How can I help you explore Laravel packages today?