Weave 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

Storage Laravel Package

sylius/storage

Sylius Storage offers a lightweight, adapter-based abstraction for file storage in Sylius and PHP applications. It separates persistence from domain logic, supports interchangeable backends such as local filesystems, and keeps file handling testable and framework-friendly.

View on GitHub
Deep Wiki
Context7

Getting Started

sylius/storage is the extracted data-storage component of the Sylius e-commerce monorepo, published as a read-only split. It is not a Laravel package: it ships no service provider, no facade, no config file. Treat it as a small PHP library that gives you a storage abstraction plus at least one concrete implementation (an in-memory store) under Sylius\Component\Storage.

Minimal steps:

composer require sylius/storage

Then read vendor/sylius/storage/src/ top to bottom before writing a line of code — the whole component is small enough to audit in ten minutes, and that audit is the documentation. Identify:

  1. The storage interface (a key/value contract: has / get / set / remove / clear-style methods).
  2. The concrete implementation(s) provided — typically an InMemoryStorage that holds everything in a PHP array.
  3. Whether any provider/factory layer exists to resolve storage by name.

There is no Laravel integration guide because there is no integration. You wire it yourself in AppServiceProvider:

$this->app->singleton(StorageInterface::class, InMemoryStorage::class);

First use case to reach for: a per-request scratch store for a pipeline of services that must not know about Laravel's Cache or Session facades. For example, a pricing or cart-calculation flow where several services accumulate intermediate values, and you want a single dependency that is trivially swappable in tests. That is exactly the shape the component was designed for inside Sylius.

Implementation Patterns

Bind the interface, not the class. Inject StorageInterface everywhere. This is the only thing that makes the package worth its weight — the implementation is replaceable, and you will almost certainly replace it.

Write a Laravel-backed adapter. The in-memory store dies at the end of the request. Map the interface onto Illuminate\Contracts\Cache\Repository so the same code path works in production:

final class CacheStorage implements StorageInterface
{
    public function __construct(private Repository $cache) {}

    public function has(string $key): bool
    {
        return $this->cache->has($key);
    }

    public function get(string $key, mixed $default = null): mixed
    {
        return $this->cache->get($key, $default);
    }

    public function set(string $key, mixed $value): void
    {
        $this->cache->forever($key, $value);
    }
    // ...
}

Keep the adapter in app/, not in vendor/. Do not fork the package.

Namespace your keys. The interface has no concept of a namespace or prefix; collisions are your problem. Adopt a convention immediately ("checkout.{$cartId}.totals") or wrap the store in a PrefixedStorage decorator.

Use it as a decorator seam. The interface is tiny, which makes it an excellent place to insert logging, metrics, or an array-cache front:

final class LoggingStorage implements StorageInterface
{
    public function __construct(
        private StorageInterface $inner,
        private LoggerInterface $log,
    ) {}

    public function set(string $key, mixed $value): void
    {
        $this->log->debug('storage.set', ['key' => $key, 'type' => get_debug_type($value)]);
        $this->inner->set($key, $value);
    }
}

Test with the bundled in-memory implementation. This is the package's one genuinely pleasant property: new InMemoryStorage() is a zero-dependency, zero-cleanup test double. Swap it in via $this->app->instance(StorageInterface::class, new InMemoryStorage()) in a setUp() or bind() call.

Serialization discipline. If you back the store with cache, session, or a database column, every value must be serializable. InMemoryStorage will happily hold a closure or an Eloquent model; CacheStorage will not. Decide early whether the contract permits arbitrary objects or only scalars/arrays, and enforce it in the adapter, not at call sites.

Gotchas and Tips

It is archived. The repository is read-only and Dependents: 0. There will be no bug fixes, no PHP-version bumps, no security patches. Accept that you are vendoring a frozen contract. If a future PHP release breaks it, you own the patch — realistically by copying the three or four files into app/Support/ and dropping the dependency.

Issues and PRs go upstream. Any PR to a read-only split is closed and redirected to Sylius/Sylius. Do not plan contribution work against this repo.

The split can lag the monorepo. Read-only splits are generated; a tagged version here may not correspond exactly to the same tag in sylius/sylius. If you need the canonical source, read the monorepo.

The name is misleading. sylius/storage is not a filesystem storage, not a Flysystem wrapper, and not an upload handler. It is a key/value abstraction used internally for accumulated state. If you are looking for file storage, you want league/flysystem, not this.

InMemoryStorage is per-instance, not per-application. Registered as a singleton it survives the request; registered as a bind() it is fresh on every resolution. That difference silently changes behaviour depending on whether a service is resolved once or repeatedly. Prefer singleton unless you have a reason, and add a test that asserts state persists across two resolutions.

No TTL, no expiry, no eviction. There is nothing in the interface for lifetime management. If you need expiry you must implement it in the adapter (delegate to Cache::put($key, $value, $ttl)) or wrap the store with a timestamped value object. Do not assume a set implies persistence forever.

No atomicity or concurrency control. There is no compare-and-swap. Two concurrent requests writing the same key last-write-wins with no warning. Fine for request-scoped scratch data; dangerous the moment you point it at a shared cache.

Debugging is awkward because the interface is so thin. When a value appears "missing," you cannot tell whether it was never written, was written under a different key, was evicted, or was stored unserializable. Add the logging decorator above before you have a bug. Always log get_debug_type($value) alongside the key — unserializable objects are the most common silent failure.

Extension points are minimal by design. There is no plugin system, no event dispatcher, no config tree. The intended extension mechanism is exactly what the interface suggests: implement StorageInterface yourself. That is also the escape hatch if the bundled implementations do not fit — which, in Laravel, they almost certainly do not, since you already have Cache, Session, and Database.

Cost/benefit call. With a score of 0.03 and zero dependents, adopting this adds a dead dependency to gain roughly one interface definition. Unless you are mirroring Sylius internals for a migration or an e-commerce integration, define the same three-method interface in your own app/Contracts/ and skip the package entirely — you get identical decoupling, full ownership, and no abandoned-vendor risk.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons