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

Psr 6 Doctrine Bridge Laravel Package

cache/psr-6-doctrine-bridge

Adapts any PSR-6 cache pool to Doctrine's legacy Cache interface. Install via Composer, then use contains, fetch, save and delete as usual; falls back to SHA-256 keys when the pool rejects a key. Includes getCachePool access.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a small adapter: it implements Doctrine’s legacy Doctrine\Common\Cache\Cache interface on top of any PSR-6 CacheItemPoolInterface. In a Laravel app it matters only if you have Doctrine ORM components that still expect Doctrine’s cache API but you want to back them with a PSR-6 pool. Pure Eloquent apps should skip it.

Minimal path:

  1. Install the bridge and a PSR-6 pool implementation (Symfony Cache is a convenient source of pools):
composer require cache/psr-6-doctrine-bridge symfony/cache
  1. Smoke-test the bridge with an in-memory pool before touching Doctrine:
use Cache\Bridge\Doctrine\DoctrineCacheBridge;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

$bridge = new DoctrineCacheBridge(new ArrayAdapter());

$bridge->save('smoke', ['ok' => true], 60);
var_dump($bridge->contains('smoke')); // true
var_dump($bridge->fetch('smoke'));    // ['ok' => true]
$bridge->delete('smoke');
  1. Create a production PSR-6 pool. With Laravel’s Redis connection and Symfony’s Redis adapter:
use Symfony\Component\Cache\Adapter\RedisAdapter;

$pool = new RedisAdapter(
    Redis::connection()->client(),
    'doctrine', // namespace/prefix
    0
);

$bridge = new DoctrineCacheBridge($pool);
  1. Wire the bridge into the Doctrine Configuration where the EntityManager is built, before the EM is used:
$config = $em->getConfiguration();
$config->setMetadataCacheImpl($bridge);
$config->setQueryCacheImpl($bridge);
// $config->setResultCacheImpl($bridge); // only when you own invalidation

Where to look first: the README for the public methods, the DoctrineCacheBridge class source for exact key/TTL behavior, and composer.json for the supported doctrine/cache and PSR versions. First real use case: move Doctrine metadata caching to Redis while keeping Doctrine ORM 2.x code unchanged.

Implementation Patterns

Bind the bridge as a singleton. In Laravel, keep one pool/bridge per Doctrine cache purpose:

// AppServiceProvider::register()
$this->app->singleton(DoctrineCacheBridge::class, function () {
    $pool = new \Symfony\Component\Cache\Adapter\RedisAdapter(
        \Redis::connection()->client(),
        'doctrine'
    );

    return new DoctrineCacheBridge($pool);
});

Then resolve it in the same factory/service provider that builds the Doctrine Configuration. If your Doctrine integration only accepts string cache driver names, register a custom driver or extend its cache manager; setting setMetadataCacheImpl() after the EM has already created its metadata factory may be too late.

Reuse Laravel’s cache stores via PSR-16. Laravel’s cache repository implements PSR-16. Symfony can wrap a PSR-16 cache as a PSR-6 pool:

use Symfony\Component\Cache\Adapter\Psr16Adapter;

$pool = new Psr16Adapter(Cache::store('redis'));
$bridge = new DoctrineCacheBridge($pool);

This is convenient when you want Doctrine metadata/query cache to share Laravel’s cache configuration. It is a thinner abstraction than using a native PSR-6 pool, so validate TTLs and key handling with your actual driver.

Prefer a dedicated pool for Doctrine. The bridge does not expose PSR-6 namespaces/tags, so mixing Laravel application keys and Doctrine keys in one pool makes debugging and invalidation harder. Use a separate Redis DB/prefix or a separate pool per cache type:

  • metadata cache: long TTL, safe to clear on deploy
  • query cache: DQL parsing results, usually safe to clear on deploy
  • result cache: application data, requires deliberate invalidation

Testing pattern. Use ArrayAdapter in unit tests and assert the Doctrine-facing behavior:

$bridge = new DoctrineCacheBridge(new ArrayAdapter());

$this->assertFalse($bridge->fetch('missing'));
$bridge->save('key', ['value'], 60);
$this->assertTrue($bridge->contains('key'));
$this->assertSame(['value'], $bridge->fetch('key'));

For integration tests, use a real Redis pool and load an entity twice; the second load should hit metadata cache. Clear the pool between test cases.

Deploy/clear pattern. Because the bridge does not implement Doctrine’s ClearableCache, CLI commands that call deleteAll() may not clear the pool. Either clear the dedicated pool directly ($bridge->getPool()->clear()) during deploy, or create a small wrapper that implements ClearableCache and delegates to clear(). Keep this pool dedicated if you do that.

Extension point. getPool() gives you the underlying PSR-6 pool for health checks, metrics, or direct clearing. Use it for operational tooling, but avoid writing application cache entries into the same key space as Doctrine.

Gotchas and Tips

Check your Doctrine version first. This bridge is for code that still expects Doctrine\Common\Cache\Cache. Doctrine ORM 3.x and newer Doctrine cache integrations may use PSR-6 directly, making the bridge unnecessary. Pin and test doctrine/orm, doctrine/cache, and the bridge together; doctrine/cache 2.x added stricter signatures, so an old bridge release can break on return types.

PSR-6 reserved characters are the first failure point. PSR-6 reserves {}()/\@: in keys. Doctrine metadata keys are often fully qualified class names containing \. Test early:

$bridge->save('App\\Entity\\User', ['meta'], 60);

If your pool throws InvalidArgumentException, the bridge either needs to normalize keys or you need a pool decorator that does. The bridge is small; forking it to replace reserved characters in fetch/contains/save/delete is often the fastest fix.

TTL 0 means “forever” in Doctrine. Verify that the bridge does not call expiresAfter(0), which some PSR-6 pools treat as immediate expiration. A quick integration test against your production pool is worthwhile.

fetch() returns false on a miss. If you cache the literal value false, you cannot distinguish a hit from a miss through the Doctrine interface. Doctrine metadata/query cache values are usually arrays/objects, so this is rarely an issue.

Namespaces and stats are not reliable. setNamespace() may be a no-op and getStats() may return empty/null. Do not build operational dashboards on the bridge’s stats. Use Redis INFO, Symfony Cache metrics, or a pool decorator that logs hits/misses.

No tags. PSR-6 tags are not part of the spec and the bridge does not expose them. Doctrine result cache invalidation must be manual (delete($key)), or you must use a tag-aware pool outside the bridge and manage tags in your own code.

No multi-get/multi-put. The bridge implements the basic Cache interface, not Doctrine’s MultiGetCache/MultiPutCache. Doctrine metadata loading may do contains() followed by fetch(), which is two cache round trips per class. That is usually fine for metadata, but benchmark if you are replacing a native multi-get cache in a hot path.

Serialization matters. Doctrine caches metadata objects and query parsing results. Use a pool that serializes values correctly for production (Redis, filesystem, etc.). ArrayAdapter is only for tests or short-lived processes; it will grow unbounded in long-running workers.

Clear old cache after schema changes. Metadata cache contains mapping information. After migrations or entity mapping changes, clear the dedicated Doctrine pool before warming. If you reuse a shared Laravel cache store, make sure the prefix or database is distinct enough that a broad Cache::flush() does not wipe unrelated application cache.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin