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

Pando Product Sale Bundle Laravel Package

blackboxcode/pando-product-sale-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation Add the bundle to your Laravel project via Composer:

    composer require blackboxcode/pando-product-sale-bundle
    

    Register the bundle in config/app.php under providers:

    BlackBoxCode\PandoProductSaleBundle\PandoProductSaleServiceProvider::class,
    
  2. Publish Configuration Run the vendor publish command to generate the default config:

    php artisan vendor:publish --provider="BlackBoxCode\PandoProductSaleBundle\PandoProductSaleServiceProvider" --tag="config"
    

    This creates config/pando_product_sale.php.

  3. First Use Case: Creating a Product Sale Define a sale in your config/pando_product_sale.php:

    'sales' => [
        'summer_discount' => [
            'name' => 'Summer Sale',
            'discount_type' => 'percentage', // or 'fixed'
            'discount_value' => 20, // 20% or 20.00
            'start_date' => '2023-06-01',
            'end_date' => '2023-08-31',
            'applicable_products' => ['product_id_1', 'product_id_2'],
        ],
    ],
    

    Use the facade in a controller or service:

    use BlackBoxCode\PandoProductSaleBundle\Facades\PandoProductSale;
    
    $sale = PandoProductSale::getSaleForProduct($productId);
    $discountedPrice = PandoProductSale::calculateDiscountedPrice($originalPrice, $sale);
    

Implementation Patterns

Core Workflows

  1. Dynamic Discount Application Integrate with your cart/checkout logic:

    // In a CartService or similar
    public function applyDiscounts($cartItems)
    {
        $discountedItems = collect($cartItems)->map(function ($item) {
            $sale = PandoProductSale::getSaleForProduct($item['product_id']);
            $item['price'] = PandoProductSale::calculateDiscountedPrice($item['price'], $sale);
            return $item;
        });
    
        return $discountedItems;
    }
    
  2. Conditional Sale Logic Extend the bundle’s logic with custom conditions:

    // Example: Apply sale only if user is a premium member
    $sale = PandoProductSale::getSaleForProduct($productId);
    if ($sale && auth()->user()->isPremium()) {
        $discountedPrice = PandoProductSale::calculateDiscountedPrice($originalPrice, $sale);
    }
    
  3. Event-Based Triggers Listen for sale-related events (if the bundle emits them):

    // config/events.php
    'BlackBoxCode\PandoProductSaleBundle\Events\SaleApplied' => [
        'App\Listeners\LogSaleApplication',
    ],
    
  4. API Integration Expose sale data via API:

    Route::get('/api/sales', function () {
        return PandoProductSale::getActiveSales();
    });
    

Integration Tips

  1. Database Backing (Optional) If you need dynamic sales, extend the bundle to store sales in a database table (e.g., sales). Override the config loader to fetch from the DB:

    // app/Providers/PandoProductSaleServiceProvider.php
    public function boot()
    {
        $this->app->bind('pando.sale.repository', function () {
            return new DatabaseSaleRepository();
        });
    }
    
  2. Product-Sale Relationships Use Eloquent relationships if products/sales are models:

    // Product model
    public function sales()
    {
        return $this->belongsToMany(Sale::class, 'product_sale');
    }
    
  3. Caching Active Sales Cache active sales to avoid repeated config checks:

    $activeSales = Cache::remember('active_sales', now()->addHours(1), function () {
        return PandoProductSale::getActiveSales();
    });
    
  4. Localization Localize sale names/discounts:

    $saleName = __('sales.' . $sale['name']);
    

Gotchas and Tips

Pitfalls

  1. Config Overrides

    • Issue: Changes to config/pando_product_sale.php may not reflect immediately if the config is cached.
    • Fix: Clear the config cache:
      php artisan config:clear
      
  2. Date Handling

    • Issue: Sales with start_date/end_date in the past/future may not behave as expected.
    • Fix: Validate dates in your config or use Carbon for comparisons:
      use Carbon\Carbon;
      $isActive = Carbon::parse($sale['end_date'])->isAfter(now());
      
  3. Product ID Mismatches

    • Issue: Hardcoded product IDs in config may break if IDs change.
    • Fix: Use product slugs or dynamic lookups:
      $product = Product::where('slug', $sale['applicable_products'][0])->first();
      
  4. Discount Calculation Edge Cases

    • Issue: Fixed discounts may not handle decimal prices correctly (e.g., $10.99 - $5.00 = $5.99).
    • Fix: Round results or use bcmath for precision:
      $discountedPrice = number_format($originalPrice - $sale['discount_value'], 2);
      
  5. Bundle Autoloading

    • Issue: If the bundle isn’t autoloaded, use explicit namespace paths:
      use BlackBoxCode\PandoProductSaleBundle\PandoProductSale;
      

Debugging Tips

  1. Log Sale Data Add debug logs to verify sale retrieval:

    \Log::debug('Active sales:', PandoProductSale::getActiveSales());
    
  2. Check Config Loading Verify the config is loaded correctly:

    \Log::debug('Pando config:', config('pando_product_sale'));
    
  3. Validate Discount Logic Test edge cases:

    • Zero-discount sales.
    • Sales with discount_type not set.
    • Products not in any sale.
  4. Use Tinker for Testing Test sale calculations interactively:

    php artisan tinker
    
    >> $sale = PandoProductSale::getSaleForProduct(1);
    => ['name' => 'Summer Sale', ...]
    >> PandoProductSale::calculateDiscountedPrice(100, $sale);
    => 80.00
    

Extension Points

  1. Custom Sale Types Extend the bundle to support custom discount types (e.g., "buy X get Y free"):

    // app/Services/CustomDiscountCalculator.php
    public function calculate($price, $sale)
    {
        if ($sale['discount_type'] === 'buy_x_get_y') {
            return $price - ($price / $sale['discount_value']['x']);
        }
        return PandoProductSale::calculateDiscountedPrice($price, $sale);
    }
    
  2. Repository Pattern Replace the default config-based repository with a database or API-driven one:

    // app/Repositories/SaleRepository.php
    public function getActiveSales()
    {
        return Sale::where('end_date', '>', now())
                    ->where('start_date', '<=', now())
                    ->get();
    }
    
  3. Event System Add events for sale creation/activation:

    // In PandoProductSaleServiceProvider
    event(new \BlackBoxCode\PandoProductSaleBundle\Events\SaleActivated($sale));
    
  4. API Endpoints Create RESTful endpoints for managing sales:

    Route::apiResource('sales', \App\Http\Controllers\SaleController::class);
    
  5. Admin Panel Integration Use the bundle’s data in admin panels (e.g., Laravel Nova, Filament):

    // Nova Resource
    public static $fields = [
        Text::make('Discount Type', 'discount_type'),
        Text::make('Discount Value', 'discount_value'),
        // ...
    ];
    
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.
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
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope