Installation Add the bundle via Composer in your Laravel project:
composer require atm/promotoolsbundle
Register the bundle in config/app.php under providers:
Atm\PromoToolsBundle\PromoToolsServiceProvider::class,
Publish Config Run the Artisan command to publish the default configuration:
php artisan vendor:publish --provider="Atm\PromoToolsBundle\PromoToolsServiceProvider"
This generates a promotools.php config file in config/.
First Use Case Trigger a basic promo evaluation for a user:
use Atm\PromoToolsBundle\PromoEvaluator;
$evaluator = app(PromoEvaluator::class);
$result = $evaluator->evaluatePromos($userId, $requestData);
Check the config/promotools.php for available promo types and their default rules.
Promo Evaluation
$request = [
'user_id' => 123,
'products' => [101, 102],
'metadata' => ['campaign_id' => 'summer2023']
];
[
'promo_id' => 'discount_10_percent',
'discount' => 10.0,
'applicable' => true,
'rules' => ['min_cart_value' => 50.0]
]
Custom Rule Integration
Extend the bundle’s rule system by implementing Atm\PromoToolsBundle\Contracts\PromoRuleInterface:
namespace App\PromoRules;
use Atm\PromoToolsBundle\Contracts\PromoRuleInterface;
class CustomUserTierRule implements PromoRuleInterface
{
public function evaluate($userId, $context): bool
{
// Custom logic (e.g., check user tier from a database)
return User::find($userId)->tier === 'premium';
}
}
Register the rule in config/promotools.php under rules.
Promo Creation
Define promos in the config or dynamically via the PromoManager:
$promoManager = app(\Atm\PromoToolsBundle\PromoManager::class);
$promoManager->createPromo([
'id' => 'black_friday_2023',
'type' => 'percentage_discount',
'value' => 20.0,
'rules' => ['date_range', 'custom_user_tier']
]);
Event-Driven Promos
Listen for events (e.g., promo.applied) to trigger side effects:
Event::listen('promo.applied', function ($promo, $user, $context) {
// Log promo application or send notifications
Log::info("Promo {$promo['id']} applied to user {$user->id}");
});
PromoRepository interface to fetch promos from your database instead of the config.$cacheKey = "promos_{$userId}_{$requestHash}";
$result = Cache::remember($cacheKey, now()->addHours(1), function() use ($evaluator, $request) {
return $evaluator->evaluatePromos($userId, $request);
});
PromoEvaluator directly in unit tests to mock promo logic:
$evaluator = $this->app->make(PromoEvaluator::class);
$evaluator->setPromos([/* mock promos */]);
Rule Evaluation Order
Rules are evaluated in the order defined in config/promotools.php under rule_order. Ensure critical rules (e.g., date_range) appear first to avoid false positives.
Context Mismatch
The $context passed to rules must match the structure expected by the rule. For example, a product_category rule expects a categories key in the request array.
Circular Dependencies Avoid circular references in custom rules (e.g., Rule A calls Rule B, which calls Rule A). Use a dependency injection container to manage rule instantiation.
Performance with Complex Rules Rules that query external services (e.g., APIs) can bottleneck evaluations. Cache results or use async processing for non-critical rules.
Enable Logging
Set debug to true in config/promotools.php to log rule evaluations:
'debug' => env('APP_DEBUG', false),
Logs appear in storage/logs/laravel.log.
Validate Request Structure
Use the PromoValidator to check if a request meets the expected schema:
$validator = app(\Atm\PromoToolsBundle\PromoValidator::class);
$errors = $validator->validate($requestData);
Rule-Specific Errors
Custom rules should throw Atm\PromoToolsBundle\Exceptions\RuleEvaluationException for clarity:
throw new RuleEvaluationException("User tier not found for ID {$userId}");
Custom Promo Types
Extend Atm\PromoToolsBundle\PromoTypes\AbstractPromoType to add new discount logic (e.g., "buy X get Y free").
Rule Composers
Combine rules dynamically using the RuleComposer:
$composer = new RuleComposer();
$composer->addRule(new DateRangeRule())
->addRule(new CustomUserTierRule());
$result = $composer->evaluate($userId, $context);
Promo Storage
Override the default PromoRepository to store promos in a custom database table or external service.
Event Extensions
Dispatch custom events (e.g., promo.validation.failed) to integrate with notification systems or analytics:
Event::dispatch(new PromoValidationFailed($promoId, $userId, $errors));
How can I help you explore Laravel packages today?