blackboxcode/pando-product-sale-bundle
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,
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.
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);
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;
}
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);
}
Event-Based Triggers Listen for sale-related events (if the bundle emits them):
// config/events.php
'BlackBoxCode\PandoProductSaleBundle\Events\SaleApplied' => [
'App\Listeners\LogSaleApplication',
],
API Integration Expose sale data via API:
Route::get('/api/sales', function () {
return PandoProductSale::getActiveSales();
});
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();
});
}
Product-Sale Relationships Use Eloquent relationships if products/sales are models:
// Product model
public function sales()
{
return $this->belongsToMany(Sale::class, 'product_sale');
}
Caching Active Sales Cache active sales to avoid repeated config checks:
$activeSales = Cache::remember('active_sales', now()->addHours(1), function () {
return PandoProductSale::getActiveSales();
});
Localization Localize sale names/discounts:
$saleName = __('sales.' . $sale['name']);
Config Overrides
config/pando_product_sale.php may not reflect immediately if the config is cached.php artisan config:clear
Date Handling
start_date/end_date in the past/future may not behave as expected.use Carbon\Carbon;
$isActive = Carbon::parse($sale['end_date'])->isAfter(now());
Product ID Mismatches
$product = Product::where('slug', $sale['applicable_products'][0])->first();
Discount Calculation Edge Cases
bcmath for precision:
$discountedPrice = number_format($originalPrice - $sale['discount_value'], 2);
Bundle Autoloading
use BlackBoxCode\PandoProductSaleBundle\PandoProductSale;
Log Sale Data Add debug logs to verify sale retrieval:
\Log::debug('Active sales:', PandoProductSale::getActiveSales());
Check Config Loading Verify the config is loaded correctly:
\Log::debug('Pando config:', config('pando_product_sale'));
Validate Discount Logic Test edge cases:
discount_type not set.Use Tinker for Testing Test sale calculations interactively:
php artisan tinker
>> $sale = PandoProductSale::getSaleForProduct(1);
=> ['name' => 'Summer Sale', ...]
>> PandoProductSale::calculateDiscountedPrice(100, $sale);
=> 80.00
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);
}
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();
}
Event System Add events for sale creation/activation:
// In PandoProductSaleServiceProvider
event(new \BlackBoxCode\PandoProductSaleBundle\Events\SaleActivated($sale));
API Endpoints Create RESTful endpoints for managing sales:
Route::apiResource('sales', \App\Http\Controllers\SaleController::class);
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'),
// ...
];
How can I help you explore Laravel packages today?