Installation Run:
composer require ekyna/cart-bundle
Add to config/bundles.php:
Ekyna\CartBundle\EkynaCartBundle::class => ['all' => true],
Publish Config
php artisan vendor:publish --provider="Ekyna\CartBundle\EkynaCartBundle" --tag="config"
This generates config/ekyna_cart.php. Update with your cart settings (e.g., session driver, cart lifetime).
First Use Case: Adding Items
Inject the CartManager service into a controller or service:
use Ekyna\CartBundle\Manager\CartManager;
public function __construct(private CartManager $cartManager) {}
public function addToCart(Request $request, $productId, $quantity = 1) {
$product = $this->productRepository->find($productId);
$this->cartManager->add($product, $quantity);
return redirect()->back()->with('success', 'Added to cart!');
}
Viewing the Cart
Use the Cart service to render cart contents in a Blade template:
@foreach($cart->getItems() as $item)
<li>{{ $item->getProduct()->getName() }} ({{ $item->getQuantity() }})</li>
@endforeach
Cart Operations
$cartManager->add($product, $quantity)$cartManager->update($productId, $newQuantity)$cartManager->remove($productId)$cartManager->clear()Session-Based Cart
The bundle uses Symfony’s session component by default. Configure the session driver in config/ekyna_cart.php:
'driver' => 'session', // or 'database', 'redis', etc. (if extended)
'lifetime' => 86400, // 24 hours
Product Integration
Ensure your Product entity implements Ekyna\CartBundle\Model\ProductInterface:
use Ekyna\CartBundle\Model\ProductInterface;
class Product implements ProductInterface {
public function getId(): string|int { ... }
public function getName(): string { ... }
public function getPrice(): float { ... }
public function getImageUrl(): ?string { ... }
}
Cart Events
Listen for cart events (e.g., cart.item.added) via Symfony’s event dispatcher:
$eventDispatcher->addListener('cart.item.added', function (CartEvent $event) {
// Log or notify when an item is added
});
API Integration
For APIs, use the CartManager to manipulate carts via HTTP requests:
// Add to cart via API
$request->request->add(['product_id' => 123, 'quantity' => 2]);
$this->cartManager->addFromRequest($request);
Custom Cart Storage
Extend the CartStorageInterface to use a database or Redis:
class DatabaseCartStorage implements CartStorageInterface {
public function save(Cart $cart) { ... }
public function load(string $cartId): ?Cart { ... }
}
Bind it in config/ekyna_cart.php:
'storage' => Ekyna\CartBundle\Storage\DatabaseCartStorage::class,
Cart Validation Validate cart items before checkout (e.g., stock availability):
$validator = new CartValidator();
$errors = $validator->validate($cart);
if ($errors->count() > 0) {
throw new \RuntimeException('Cart validation failed');
}
Multi-Cart Support
Use the CartManager to switch between carts (e.g., for guest vs. logged-in users):
$guestCart = $cartManager->createCart('guest_' . $request->ip());
$userCart = $cartManager->createCart('user_' . auth()->id());
Checkout Workflow
$cart->getTotal()$cart->applyDiscount($code)$cartManager->save($cart);
Session Driver Issues
SESSION_DRIVER is configured in .env (e.g., SESSION_DRIVER=file).redis or database to avoid session file locks:
SESSION_DRIVER=redis
REDIS_HOST=127.0.0.1
Product Interface Mismatch
ProductInterface will throw RuntimeException when adding items to the cart.getId(), getPrice(), etc.Cart Lifetime Expiry
lifetime seconds (default: 86400). For persistent carts (e.g., abandoned carts), extend the lifetime or switch to a database-backed storage.Concurrent Requests
Event Dispatcher Not Bound
cart.item.added) aren’t firing, ensure the EventDispatcher is bound in the bundle’s services:
# config/services.yaml
Ekyna\CartBundle\Event\CartEvents::class: ~
Log Cart Contents Add a temporary route to inspect the cart:
Route::get('/debug-cart', function () {
return response()->json($this->cartManager->getCart()->getItems());
});
Check Session Data Dump session data to verify cart storage:
dd($request->getSession()->all());
Validate Product Data Ensure product IDs/prices are serializable (no circular references or non-public properties).
Custom Cart Item Model
Extend Ekyna\CartBundle\Model\CartItem to add metadata (e.g., custom attributes):
class CustomCartItem extends CartItem {
public function setAttribute(string $key, $value) { ... }
public function getAttribute(string $key) { ... }
}
Plugin System
Create a CartPlugin interface to add functionality (e.g., taxes, shipping):
interface CartPlugin {
public function apply(Cart $cart);
}
Register plugins in config/ekyna_cart.php:
'plugins' => [
Ekyna\CartBundle\Plugin\TaxPlugin::class,
App\Plugin\CustomShippingPlugin::class,
],
Override Cart Storage Replace the default storage with a custom implementation (e.g., for analytics):
$cartManager->setStorage(new AnalyticsCartStorage());
Localization
Extend the bundle’s translation files (resources/translations) for multi-language support.
Default Cart ID
The bundle uses default as the cart ID by default. Override in config:
'default_cart_id' => 'user_cart_' . auth()->id(),
Floating-Point Precision
Use round() when calculating totals to avoid precision issues:
$total = round($cart->getTotal(), 2);
Lazy Loading The bundle may lazy-load cart items. Eager-load in templates:
$cart->getItems()->load('product'); // If using Eloquent
How can I help you explore Laravel packages today?