Weave Code
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor