openclassrooms/doctrine-cache-extension-bundle
A Symfony bundle from OpenClassrooms that integrates Doctrine Cache extension capabilities, providing configuration and services to simplify caching for Doctrine-based applications. It helps wire cache providers and related services into Symfony projects.
Reality check first. This is a Symfony2 bundle, not a Laravel package. It extends Doctrine's cache layer with additional cache providers and wires them into the Symfony DI container. There is no Illuminate\Support\ServiceProvider, no facade, no config/ file that Laravel's package discovery will pick up. Installing it in a Laravel app does nothing until you manually reach into it.
Step 1 — install and inspect.
composer require openclassrooms/doctrine-cache-extension-bundle
Then look at, in this order:
vendor/openclassrooms/doctrine-cache-extension-bundle/*Bundle.php — confirm it extends Symfony\Component\HttpKernel\Bundle\Bundle. That tells you it will never self-register in Laravel.DependencyInjection/*Extension.php and Resources/config/services.* — this is where the actual cache provider services and the config tree (the bundle's config key) are defined. This is the only part that matters to a Laravel dev.Doctrine\Common\Cache\CacheProvider) — these are plain PHP classes you can instantiate from Laravel.Step 2 — decide your integration path. There are only two:
AppKernel/bundles.php, configure under its config key, inject the cache services).Illuminate\Support\Facades\Cache. The bundle's value-add (service registration, config tree) is Symfony-only.First use case — a cache-aside wrapper around a slow query or remote API call:
use Doctrine\Common\Cache\CacheProvider;
// A provider class shipped/aliased by the bundle's vendor dir
$cache = new \OpenClassrooms\DoctrineCacheExtension\Provider\SomeCacheProvider();
$cache->setNamespace('orders:v1');
$orders = $cache->fetch($key);
if ($orders === false) {
$orders = Order::with('items')->where('status', 'paid')->get()->toArray();
$cache->save($key, $orders, 300); // 5 min TTL
}
Bind it once in a service provider so you don't re-instantiate per call:
// app/Providers/AppServiceProvider.php
$this->app->singleton(CacheProvider::class, function () {
$cache = new \OpenClassrooms\DoctrineCacheExtension\Provider\SomeCacheProvider();
$cache->setNamespace(config('app.name') . ':cache');
return $cache;
});
Decorator around repositories/queries (the main pattern). Keep cache logic out of controllers and models. Wrap the data access:
final class CachedOrderRepository
{
public function __construct(
private OrderRepository $inner,
private CacheProvider $cache,
) {}
public function paid(): array
{
$key = 'paid:' . md5(json_encode([request()->query()]));
if (($hit = $this->cache->fetch($key)) !== false) {
return $hit;
}
$rows = $this->inner->paid()->toArray(); // cache arrays, not models
$this->cache->save($key, $rows, 300);
return $rows;
}
}
Bind the decorator in a service provider and let the concrete repository stay cache-agnostic.
Provider selection from config. Mirror config/cache.php with a factory so you can swap providers per environment (Redis in prod, ArrayCache in tests):
$this->app->singleton(CacheProvider::class, function ($app) {
$cache = $app->make(config('cache.doctrine.provider')); // class-string
$cache->setNamespace(config('cache.prefix'));
return $cache;
});
Tags / namespaces for invalidation. Doctrine providers support setNamespace() and deleteAll()/flushAll() (provider-dependent). Namespace per aggregate (orders:v2) and bump the version suffix on schema changes rather than trying to surgically invalidate.
Cache-aside in HTTP clients. The same decorator shape works around a Guzzle client for third-party payloads — this is often where a Doctrine provider is genuinely nicer than Laravel's facade because you get fetch()'s false miss semantics and provider-level stats.
Testing. Swap the bound provider to Doctrine\Common\Cache\ArrayCache in the test container so each test starts cold and assertions don't hit Redis.
When to not use it. If you're not porting Symfony2 code, Cache::remember() already does cache-aside with tags, locking, and multiple stores. The only reason to reach for this package is a specific provider behavior the bundle exposes that Laravel's stores don't.
extra.laravel.providers. A Symfony Bundle subclass is invisible to it; no error, it just silently does nothing.Doctrine\Common\Cache\CacheProvider and the fetch/save/contains/delete API are removed in doctrine/cache 2.0; the modern replacement is PSR-6/PSR-16 via symfony/cache. Check composer.lock — this bundle's constraint likely pins doctrine/cache ^1.x, which conflicts with anything pulling 2.x. Expect composer why-not fights.mcrypt/Memcache drivers, and no security fixes. Audit before adding to a production Laravel app — a composer audit and a php -l pass over the provider classes costs five minutes.serialize(); connections, dates, and closures in attributes will break on read. Cache arrays/DTOs only, or explicitly implement __serialize/__sleep.fetch() returns false on miss — a legitimately cached false or 0 is indistinguishable. Wrap values (['v' => $value]) or use contains() before fetch().setNamespace() you will collide in shared Redis/Memcached instances across apps and environments. Always prefix with app name + environment, and version it.save() returns a bool; check it. Silent write failures (full Memcached, oversized value >1MB, Redis maxmemory) are the most common "cache doesn't work" bug. For debugging, getStats() on providers that support it, and log save() failures.cache:clear console command in Laravel. The bundle's console integration is Symfony-only; you'll write your own artisan command calling deleteAll() or flushAll().Illuminate\Contracts\Cache\Repository and register it via Cache::extend(). Then the rest of your Laravel code uses the normal Cache facade while you get the Doctrine provider underneath.doctrine/cache 1.x providers standalone or (preferably) migrate to symfony/cache PSR-6 adapters, which are maintained and framework-agnostic.How can I help you explore Laravel packages today?