macpaw/redis-schema-bundle
Symfony bundle from MacPaw that adds a schema layer for Redis, letting you declare typed key structures and entities with serialization and lifecycle management instead of scattering raw Redis commands and key names across your application.
This is a small, single-vendor package (MacPaw) with a thin ecosystem footprint. The v2.0.1 patch is a one-line behavioural fix, not a feature release — it changes which adapter reset delegates to when the underlying cache adapter is decorated. That is a useful signal about the architecture: this bundle sits on top of a PSR-6/PSR-16-style cache adapter, and you are expected to wrap/decorate it. Plan your first hour as due diligence plus a read of the adapter wiring.
composer.json for Symfony/PHP constraint ranges against your app. Pin an exact version (2.0.1 or later) — with a package this small, unreleased-branch fixes are a realistic outcome.DependencyInjection/Configuration.php (the config surface), DependencyInjection/*Extension.php (autoconfiguration/tags), Resources/config/* (service ids you can inject), then Command/. If there is a compiler pass, that is where schemas register — and where the adapter decoration is wired.config/bundles.php if Flex doesn't, set one connection/prefix in config/packages/. Do not point it at production Redis on the first run.One schema class per key family. Keep the key template, field names, TTL, and value encoding together, so a review of that file is a review of your Redis data model. Inject the schema (or registry) into repositories and consumers — never into controllers.
Schema as the single source of key strings. The everyday win is that sprintf('user:%d:profile', $id) disappears. Grep-ability of key patterns and the ability to rename a namespace in one commit beats any type-safety claim. Pair with a repository-per-family so raw $redis->get() stays in a narrow layer.
Put expiry next to the definition. If the schema expresses TTL, apply it consistently on every write path. The most common production incident here is a key written without expiry from one code path. Audit for direct client usage that bypasses the schema.
Decorate the adapter deliberately. v2.0.1 exists precisely because reset used to go to the wrong place when the adapter was decorated. If you wrap the cache adapter (metrics, logging, prefixing), verify that a reset actually reaches your decorator and, through it, the real client — and that it doesn't nuke keys you didn't intend to clear. Add an integration test that decorates the adapter, writes keys, calls reset, and asserts the surviving key set.
CI as a lint step. If the bundle ships a validation/describe command, run it in CI (bin/console list redis to find the exact name). A command that fails on an invalid or duplicate key pattern is a cheap guard against two teams claiming cache: in different formats.
Version prefixes for migrations. Bake v1:, v2: into key templates and dual-write during rollout, reading new-first with fallback. This turns a schema change into an ordinary deploy rather than a coordinated Redis flush.
Testing. Assert generated key strings in unit tests — cheap, and catches accidental template edits. Integration-test against a real Redis container rather than a mock, because the interesting failures are TTL, prefix, cluster hash-tag behaviour, and reset/clear semantics.
Coexistence. Symfony Cache and Messenger own their own keys; don't try to model them. Keep the raw client injectable for MULTI/pipeline/Lua paths where the schema abstraction fights you.
reset vs clear is a real distinction, not a naming nit. The v2.0.1 fix ("delegate reset to decorated adapter instead of clear") means previous 2.0.0 behaviour could bypass your decorator and hit the raw client with a broad clear. If you are on 2.0.0 with a decorated adapter, upgrade and re-test any "wipe the cache namespace" code path in staging — the blast radius changes.redis-cli --scan --pattern 'prefix:*' before assuming anything. Single most likely first bug.{tag}. A schema generating user:{id}:a and user:{id}:b without a shared tag breaks MGET/Lua at runtime, not at boot.services.yaml. The compiler pass is where to look — and it's also where the adapter decoration chain is assembled, which is why the v2.0.1 bug lived in this area.MONITOR on staging to see the real command/key stream, --scan --pattern to diff declared vs live keys, and the Symfony profiler if the bundle registers a data collector. Write a small "diff declared schemas vs live keys" command early; it pays for itself the first time an old key format lingers. When diagnosing reset/clear behaviour, the command stream is more trustworthy than the bundle's method names.reset — lean into that rather than subclassing.How can I help you explore Laravel packages today?