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

Discountify Laravel Package

safemood/discountify

Laravel package for dynamic, condition-based discounts. Define custom conditions, apply percentage discounts, set global discount and tax rate, support coupon and class-based discounts, dynamic item field names, optional condition skipping, and event tracking for e-commerce totals.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:
    composer require safemood/discountify
    php artisan vendor:publish --tag="discountify-config"
    
  2. First Use Case: Define a simple discount condition in AppServiceProvider:
    use Safemood\Discountify\Facades\Condition;
    
    public function boot()
    {
        Condition::define('test_discount', fn($items) => true, 10);
    }
    
  3. Apply Discounts:
    use Safemood\Discountify\Facades\Discountify;
    
    $items = [['price' => 100, 'quantity' => 1]];
    $total = Discountify::setItems($items)->total();
    

Key Starting Points

  • Facade: Discountify for core calculations (total(), totalDetailed()).
  • Facade: Condition for defining rules.
  • Facade: Coupon for coupon-based discounts.
  • Artisan Command: discountify:condition for scaffolding class-based conditions.

Implementation Patterns

Core Workflow

  1. Define Discount Rules:

    // Inline conditions (ServiceProvider)
    Condition::define('bulk_discount', fn($items) => count($items) > 5, 15);
    
    // Class-based (auto-discovered)
    // App/Conditions/SeasonalDiscount.php
    class SeasonalDiscount implements ConditionInterface {
        public function __invoke($items) { return now()->month === 12; }
    }
    
  2. Set Cart Items:

    Discountify::setItems([
        ['price' => 50, 'quantity' => 2],
        ['price' => 100, 'quantity' => 1, 'type' => 'premium']
    ]);
    
  3. Calculate Totals:

    $total = Discountify::total(); // Basic total
    $detailed = Discountify::totalDetailed(); // Array with subtotal, tax, savings
    

Integration Tips

  • Dynamic Fields: Override default price/quantity keys via config or runtime:
    Discountify::setFields(['price' => 'amount', 'quantity' => 'qty']);
    
  • Global Adjustments:
    Discountify::setGlobalDiscount(10)->setGlobalTaxRate(20);
    
  • Coupon Flow:
    Discountify::applyCoupon('SUMMER20')->total();
    

Event-Driven Extensions

Listen for discount application events in EventServiceProvider:

Event::listen(DiscountAppliedEvent::class, function ($event) {
    // Log discounts or trigger notifications
    logger()->info('Discount applied', $event->discounts);
});

Gotchas and Tips

Common Pitfalls

  1. Slug Requirement:

    • Since v1.0.0, Condition::define() requires a slug parameter. Older code may break:
      // Old (deprecated)
      Condition::define('test', fn($items) => true, 10);
      
      // New
      Condition::define('test_slug', fn($items) => true, 10);
      
    • Fix: Update all condition definitions to include slugs.
  2. Coupon Scoping:

    • User-restricted coupons (userIds) must pass the user ID explicitly:
      Discountify::applyCoupon('EXCLUSIVE', auth()->id());
      
    • Debug: Check storage/app/discountify/coupons.json for coupon state.
  3. Field Mismatches:

    • If price/quantity keys don’t match config, calculations fail silently.
    • Tip: Use setFields() dynamically or verify config:
      $discountify->setFields(config('discountify.fields'));
      

Debugging Tips

  • Inspect Conditions:
    dd(Discountify::getConditions()); // List all registered conditions
    
  • Coupon Validation:
    if (!Discountify::validateCoupon('INVALID')) {
        // Handle invalid coupon
    }
    
  • Event Payloads: Dump DiscountAppliedEvent or CouponAppliedEvent to verify applied discounts:
    Event::listen(DiscountAppliedEvent::class, fn($e) => dd($e->discounts));
    

Performance Quirks

  • Class-Based Conditions: Auto-discovered but loaded on first use. For critical paths, eager-load:
    app()->make('Safemood\Discountify\Contracts\ConditionInterface');
    
  • Coupon Storage: Coupon state is stored in storage/app/discountify/coupons.json. Clear it for testing:
    php artisan discountify:clear-coupons
    

Extension Points

  1. Custom Condition Logic: Extend ConditionInterface for reusable logic:
    class MinCartValueDiscount implements ConditionInterface {
        public function __invoke($items) {
            return array_sum(array_column($items, 'price')) > 100;
        }
    }
    
  2. Tax Calculation Overrides: Bind a custom tax calculator:
    app()->bind('discountify.tax', fn() => new CustomTaxCalculator());
    
  3. Event Modifiers: Subscribe to DiscountAppliedEvent to adjust discounts dynamically:
    Event::listen(DiscountAppliedEvent::class, function ($event) {
        $event->discounts[] = ['slug' => 'admin_override', 'value' => 5];
    });
    
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.
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium