Installation:
composer require sylius/inventory
Add the bundle to config/bundles.php (if using Symfony) or register the service provider in config/app.php (Laravel).
Configuration:
// config/inventory.php
return [
'driver' => env('INVENTORY_DRIVER', 'doctrine_orm'), // or 'doctrine_orm', 'array', 'custom'
'stocks' => [
'default' => [
'tracking' => true, // Enable stock tracking
],
],
];
First Use Case:
use Sylius\Component\Inventory\Repository\StockRepositoryInterface;
$stockRepository = app(StockRepositoryInterface::class);
$stock = $stockRepository->findOneBy(['productId' => 'product-123']);
$quantity = $stock->getQuantity(); // Returns current stock quantity
use Sylius\Component\Inventory\Checker\StockCheckerInterface;
use Sylius\Component\Inventory\ReservableInterface;
$stockChecker = app(StockCheckerInterface::class);
$stockChecker->check($productId, $quantity); // Throws exception if insufficient stock
$reservable = app(ReservableInterface::class);
$reservable->reserve($productId, $quantity); // Reserves stock for a transaction
Product, Stock, StockEntry, InventoryUnitStockRepository, StockChecker, Reservable, InventoryUnitFactoryStock Management:
$inventoryUnit = app(\Sylius\Component\Inventory\InventoryUnit::class);
$inventoryUnit->setProductId('product-123')->setQuantity(10);
$stockRepository->save($inventoryUnit);
$stockRepository->adjust('product-123', -2); // Decrease stock by 2
$stockRepository->adjust('product-123', 5); // Increase stock by 5
Order Processing:
$orderItems = collect([['product_id' => 'product-123', 'quantity' => 3]]);
$reservable->reserveForOrder($orderItems, 'order-456');
$reservable->release('order-456'); // After order cancellation or fulfillment
Inventory Tracking:
$stockEntryRepository = app(\Sylius\Component\Inventory\Repository\StockEntryRepositoryInterface::class);
$entries = $stockEntryRepository->findBy(['productId' => 'product-123'], ['createdAt' => 'DESC']);
doctrine_orm driver and map Sylius entities to Eloquent models:
// app/Models/Product.php
class Product extends Model implements \Sylius\Component\Inventory\Model\ProductInterface
{
use \Sylius\Component\Inventory\Model\ProductTrait;
}
Route::get('/api/stock/{productId}', function ($productId) {
$stock = app(StockRepositoryInterface::class)->findOneBy(['productId' => $productId]);
return response()->json(['quantity' => $stock->getQuantity()]);
});
// app/Listeners/StockUpdatedListener.php
class StockUpdatedListener
{
public function handle($event)
{
Log::info("Stock updated for product: {$event->getProductId()}");
}
}
Register in EventServiceProvider:
protected $listen = [
\Sylius\Component\Inventory\Event\StockUpdated::class => [
StockUpdatedListener::class,
],
];
Driver Mismatch:
inventory.driver) matches your setup (e.g., doctrine_orm requires Doctrine ORM).config/inventory.php and install required dependencies (e.g., doctrine/orm for ORM driver).Stock Tracking Disabled:
tracking: false in config, stock changes won’t be logged in StockEntry.tracking: true for audit trails.Race Conditions:
DB::transaction(function () use ($stockRepository) {
$stockRepository->adjust('product-123', -1);
});
Product ID Format:
productId as a string (e.g., 'product-123'), not an integer or UUID.Enable Logging:
// config/inventory.php
'logging' => [
'enabled' => true,
'channel' => 'inventory',
],
Check logs for stock operations:
tail -f storage/logs/inventory.log
Dump Stock Data:
$stock = $stockRepository->findOneBy(['productId' => 'product-123']);
dd($stock->getQuantity(), $stock->getReservations());
Validate Stock:
Use the StockChecker to debug stock issues:
try {
$stockChecker->check('product-123', 5);
} catch (\Sylius\Component\Inventory\Exception\OutOfStockException $e) {
dd($e->getMessage()); // "Not enough stock for product-123"
}
Custom Drivers:
Implement Sylius\Component\Inventory\Driver\DriverInterface for non-Doctrine storage (e.g., Redis, Elasticsearch):
class RedisDriver implements DriverInterface
{
public function findStock($productId): ?StockInterface
{
$stock = Redis::get("stock:{$productId}");
return $stock ? json_decode($stock, true) : null;
}
// ... other methods
}
Custom Stock Entries:
Extend StockEntry to add custom fields:
class CustomStockEntry extends \Sylius\Component\Inventory\Model\StockEntry
{
protected $adjustmentReason;
public function setAdjustmentReason(string $reason): self
{
$this->adjustmentReason = $reason;
return $this;
}
}
Inventory Events: Subscribe to events for custom logic:
// Example: Notify when stock is low
event(new \Sylius\Component\Inventory\Event\StockUpdated($productId, $oldQuantity, $newQuantity));
Listen in a service:
public function __construct()
{
event(new \Sylius\Component\Inventory\Event\StockUpdated(...));
}
Laravel Service Provider:
Override default services in AppServiceProvider:
public function register()
{
$this->app->bind(
\Sylius\Component\Inventory\Checker\StockCheckerInterface::class,
\App\Services\CustomStockChecker::class
);
}
$stockRepository->bulkAdjust([
'product-123' => -10,
'product-456' => 5,
]);
$quantity = Cache::remember("stock:product-123", now()->addHours(1), function () {
return $stockRepository->findOneBy(['productId' => 'product-123'])->getQuantity();
});
How can I help you explore Laravel packages today?