Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Promotion Laravel Package

sylius/promotion

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require sylius/promotion
    

    Add the bundle to config/bundles.php (if using Symfony) or register the component manually in Laravel.

  2. First Use Case:

    • Define a promotion in your database (e.g., promotions table).
    • Use the PromotionChecker service to evaluate if a cart qualifies for a promotion:
      use Sylius\Component\Promotion\Checker\PromotionCheckerInterface;
      
      $promotionChecker = app(PromotionCheckerInterface::class);
      $isEligible = $promotionChecker->isEligible($cart, $promotion);
      
  3. Key Classes to Explore:

    • Promotion (core entity)
    • PromotionChecker (evaluates eligibility)
    • PromotionApplicator (applies discounts to carts)
    • Rule and Action (custom logic for promotions)

Implementation Patterns

Core Workflow

  1. Define Promotions:

    • Create promotions via migrations or admin panels (e.g., Sylius Admin).
    • Example promotion structure:
      $promotion = new Promotion();
      $promotion->setName('Summer Sale');
      $promotion->setDescription('10% off for orders over $100');
      $promotion->setStartsAt(new \DateTime('now'));
      $promotion->setEndsAt(new \DateTime('+1 month'));
      
  2. Rules and Actions:

    • Attach rules (e.g., CartTotalRule, ProductRule) and actions (e.g., PercentageDiscountAction):
      $rule = new CartTotalRule();
      $rule->setComparison('gt'); // Greater than
      $rule->setValue(100);       // $100
      $promotion->addRule($rule);
      
      $action = new PercentageDiscountAction();
      $action->setValue(10);      // 10%
      $promotion->addAction($action);
      
  3. Check and Apply:

    • Integrate with cart/checkout logic:
      // Check eligibility
      if ($promotionChecker->isEligible($cart, $promotion)) {
          $promotionApplicator = app(PromotionApplicatorInterface::class);
          $promotionApplicator->apply($cart, $promotion);
      }
      
  4. Coupon Support:

    • Use Coupon entities for limited-use promotions:
      $coupon = new Coupon();
      $coupon->setCode('SUMMER20');
      $coupon->setPromotion($promotion);
      $coupon->setUsageLimit(100);
      

Integration Tips

  • Laravel-Specific:

    • Bind services in AppServiceProvider:
      $this->app->bind(PromotionCheckerInterface::class, function ($app) {
          return new PromotionChecker($app->make(PromotionRepositoryInterface::class));
      });
      
    • Use Laravel’s event system to trigger promotion checks (e.g., cart.item_added).
  • Custom Rules/Actions:

    • Extend RuleInterface or ActionInterface for domain-specific logic.
    • Example custom rule:
      class LoyaltyCustomerRule implements RuleInterface {
          public function isEligible(CartInterface $cart, PromotionInterface $promotion): bool {
              return $cart->getCustomer()->isLoyal();
          }
      }
      
  • Database Schema:

    • Use Sylius’ migrations or create your own for promotion, rule, action, and coupon tables.
    • Example migration snippet:
      Schema::create('sylius_promotion', function (Blueprint $table) {
          $table->id();
          $table->string('name');
          $table->text('description')->nullable();
          $table->dateTime('starts_at')->nullable();
          $table->dateTime('ends_at')->nullable();
          // ... other fields
      });
      

Gotchas and Tips

Pitfalls

  1. Circular Dependencies:

    • Avoid rules/actions that create infinite loops (e.g., a rule that modifies cart items in a way that triggers itself).
  2. Performance:

    • Complex rules (e.g., nested conditions) can slow down cart checks. Cache promotion eligibility where possible:
      $cacheKey = "promo_eligible_{$promotion->getId()}_{$cart->getId()}";
      if (cache()->has($cacheKey)) {
          return cache()->get($cacheKey);
      }
      
  3. Time-Based Promotions:

    • Ensure starts_at/ends_at are handled correctly in your timezone. Use Carbon for consistency:
      $now = Carbon::now();
      if ($promotion->getStartsAt() && $promotion->getStartsAt()->gt($now)) {
          return false;
      }
      
  4. Coupon Exhaustion:

    • Track coupon usage limits in real-time to avoid over-issuance. Use database transactions for safety:
      DB::transaction(function () use ($coupon) {
          if ($coupon->getUsageLimit() && $coupon->getUsageCount() >= $coupon->getUsageLimit()) {
              throw new \RuntimeException('Coupon limit reached');
          }
          $coupon->incrementUsageCount();
      });
      

Debugging

  1. Eligibility Issues:

    • Log rule evaluations to debug why a promotion isn’t applying:
      foreach ($promotion->getRules() as $rule) {
          logger()->debug(
              sprintf('Rule %s (%s) eligible: %s',
                  get_class($rule),
                  $rule->getConfiguration(),
                  $rule->isEligible($cart, $promotion)
              )
          );
      }
      
  2. Action Conflicts:

    • If multiple actions apply to the same cart item, ensure they don’t override each other unintentionally. Use priority flags or merge strategies.
  3. Database Constraints:

    • Foreign keys between promotion, rule, and action tables may cause issues if not set up correctly. Use Sylius’ migrations as a reference.

Extension Points

  1. Custom Rule/Action Storage:

    • Store rule/action configurations in JSON fields (e.g., configuration) for flexibility:
      $rule->setConfiguration(['operator' => 'gt', 'value' => 100]);
      
  2. Promotion Events:

    • Dispatch events for promotion lifecycle (e.g., promotion.applied, promotion.expired). Example:
      event(new PromotionApplied($cart, $promotion));
      
  3. API Integration:

    • Expose promotions via Laravel Sanctum or API Platform for headless setups:
      Route::get('/api/promotions', function () {
          return PromotionResource::collection(Promotion::all());
      });
      
  4. Testing:

    • Use Laravel’s testing tools to mock promotions:
      $promotion = $this->createMock(PromotionInterface::class);
      $promotion->method('getActions')->willReturn([$this->createMock(ActionInterface::class)]);
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style