justbetter/laravel-magento-stock
A Laravel package for integrating Magento stock data into your application. It provides convenient tools to retrieve and validate product inventory and availability, helping you display accurate stock status and support reliable checkout, ordering, and catalog decisions.
Install and wire up the client first. This package is a thin stock-lookup layer on top of justbetter/laravel-magento-client; it cannot talk to Magento on its own.
composer require justbetter/laravel-magento-stock:^2.4
php artisan vendor:publish --provider="JustBetter\MagentoStock\ServiceProvider"
Then configure the underlying Magento connection (base URL + integration token) in config/magento-client.php / .env, and the stock-specific options (source code, cache TTL, cache store) in config/magento-stock.php. Confirm connectivity before writing feature code:
php artisan tinker
>>> JustBetter\MagentoStock\Facades\MagentoStock::getStock('SKU-123');
Where to look first
config/magento-stock.php – source code (default vs a specific MSI source), cache store and TTL, placeholder behaviour for unknown SKUs.MagentoStock facade – the public surface you will actually call from controllers, models and jobs.Canonical first use case: a single SKU on a product detail page. Resolve the SKU from your Product model, call the facade, and render the salable quantity / in-stock boolean. Once that works end-to-end, you have validated credentials, source code and caching in one go, and everything else is scale.
Model accessor over raw facade calls. Wrap lookups behind your own domain object so controllers never know about Magento:
class Product extends Model
{
public function getStockAttribute(): StockData
{
return MagentoStock::getStock($this->sku);
}
}
This is the seam that lets you swap in a local DB cache, a Redis store, or a fake in tests without touching views.
Batch for listings and search results. Never loop over a collection calling the facade per item — that is N synchronous HTTP round-trips and will dominate your page load. Collect the SKUs and request them in one call (the package supports multi-SKU retrieval), then hydrate a sku => stock map:
$skus = $products->pluck('sku')->all();
$stock = MagentoStock::getStockForSkus($skus); // hydrate once
Map into the collection via ->map() rather than letting each model lazy-load Magento.
Cache aggressively, invalidate deliberately. Stock is read-mostly but must not be so stale you oversell. Two-tier pattern that works well:
where stock > 0) where you cannot afford remote calls at all.Queue the writes, not the reads. Anything that follows an order (reservation, decrement, re-check) belongs in a job with a retry/backoff policy, because Magento MSI calls are the part most likely to fail transiently. Reads can be synchronous with cache in front; mutations should not block the checkout response.
Gate the feature. Put stock lookups behind a config flag and a null-object implementation so local/staging environments without Magento credentials boot cleanly instead of throwing connection errors on every request.
Testing seam. Bind a fake implementation of the stock contract in your test suite and assert on your own accessors/mappers. Do not point tests at a real Magento instance — flaky and slow.
Salable quantity is not quantity. With MSI enabled, a SKU can have physical quantity in a source yet zero salable quantity due to reservations. Decide explicitly which number your UI shows; "in stock" driven by raw quantity will be wrong on any store with pending orders. Also confirm the configured source code actually exists in Magento — a mistyped source silently yields empty/zero results rather than an error.
SKU mismatches are the #1 support ticket. SKUs are case- and whitespace-sensitive between Laravel and Magento. Normalise on ingest and log the offending SKU when a lookup returns nothing, otherwise you get "all products out of stock" with no clue why.
Unknown SKUs are normal, not exceptional. Products deleted in Magento, or created in Laravel but not yet pushed, will miss. Configure the placeholder behaviour and make your code tolerate a null/zero result instead of a thrown exception on a product page.
Watch for retry/loop behaviour against the Magento API. Version 2.4.4 fixes a loop in the request path; if you are on 2.4.3 or earlier and see repeated or runaway calls to Magento (visible as duplicated entries in the client's log or a spike in request count per page load), upgrading to ^2.4.4 is the fix. It is a patch release with no API or config changes — a drop-in replacement, but re-run your test suite and watch request counts in staging for a day since the changed code is on the hot read path.
Cache TTL is a business decision. A long TTL reduces Magento load but increases the window in which you sell something you no longer have; a short TTL pushes you toward rate limits. Set it per environment (tight in production checkout paths, loose for catalogue browsing) and remember the cache key must include the source code if you query multiple sources.
Rate limits and latency. Magento's REST API is slow compared to a local DB — a 100 ms average is optimistic. Time-box calls in the request cycle, set client timeouts, and fall back to your last persisted snapshot on timeout rather than failing the page.
Extension points worth knowing: the published config (cache store/TTL, source code, placeholder value), the bound contracts behind the facade (swap for your own implementation or a decorator that adds logging/metrics), and the underlying Magento client (swap for a mocked transport in tests). When debugging, enable the Magento client's HTTP logging first — most "the package is broken" reports are auth token expiry or a 404 on the SKU, both visible in the raw response.
Version pinning. Stay on ^2.4 and read each patch's changelog: this package sits on the read path of product pages, so a subtle regression is immediately customer-visible.
How can I help you explore Laravel packages today?