Symfony: Implementace Redis cache pro výsledky Doctrine Repository

3. 6. 2026

Při vývoji aplikací v Symfony se často dostaneme do situace, kdy některá data měníme jen velmi zřídka, ale čteme je při každém requestu. Typickým příkladem jsou lokalizace, seznamy států, měn, nastavení aplikace, sazby DPH nebo různé číselníky.

Pokud se při každém načtení stránky provádí stejný SQL dotaz do databáze, zbytečně zatěžujeme databázový server a prodlužujeme dobu odezvy aplikace. Jednoduchým řešením je zařazení cache vrstvy pomocí Redis.

Architektura

Controller

    ↓

Facade

    ↓

CachedRepository

    ↓

Repository

    ↓

Database

Repository

Repository obsahuje pouze přístup k databázi. Jeho jedinou odpovědností je sestavení Doctrine QueryBuilderu a načtení dat z databáze.

final class LocalizationRepository
{
    public function findLanguageMap(): array
    {
        return $this->createQueryBuilder('l')
            ->select('l.code, l.name')
            ->getQuery()
            ->getArrayResult();
    }
}

Repository by nemělo obsahovat cache logiku. Díky tomu zůstává snadno testovatelné a znovupoužitelné.

Cached Repository

Nad repository vytvářím samostatnou vrstvu, která řeší pouze cachování.

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
…

final class LocalizationCachedRepository
{
   public function __construct(
       #[Autowire(service: 'cache.static-data')]
       private readonly TagAwareCacheInterface $cache,
       private LocalizationRepository $repository
   )
   {
   }

   public function findLanguageMap(): array
   {
       return $this->cache->get('localization.language-map', function(ItemInterface $item): array {
          
           $item->expiresAfter(86400);
           $item->tag('localization');

           return $this->repository->findLanguageMap();
       });
   }
}

Výhodou tohoto přístupu je oddělení business logiky od cache vrstvy. Pokud bude v budoucnu potřeba Redis nahradit jiným úložištěm nebo cache úplně vypnout, změna proběhne pouze na jednom místě.

Facade

Facade představuje vstupní bod pro business logiku.

class LocalizationFacade
{
    public function __construct(
        private LocalizationCachedRepository $repository,
    )
    {
    }

    public function getLanguageMap(): array
    {
        return $this->repository->findLanguageMap();
    }
}

Controller už nemusí vědět, zda data pocházejí z databáze nebo z cache.

Konfigurace Redis cache

Symfony nabízí velmi jednoduchou integraci Redis přes Cache komponentu.

# config/packages/cache.yaml

framework:
    cache:
        # Unikátní prefix cache klíčů pro oddělení aplikací v Redisu
        # nebo oddělení módů - test, stage, prod apod…
        prefix_seed: 'myapp'

        app: cache.adapter.redis
        default_redis_provider: 'redis://127.0.0.1:6379'

        pools:
            cache.static-data:
                adapter: cache.adapter.redis
                default_lifetime: 86400
                tags: true # Umožňuje tagovat cache pro hromadnou invalidaci

Invalidace cache

Největší problém cache není její vytvoření, ale správná invalidace. Pokud se data změní, je potřeba odpovídající cache odstranit.

$cache->delete('localization.language-map');

Při použití tagů:

$tagAwareCache->invalidateTags([
    'localization',
]);

Výhodou tagů je možnost jedním příkazem invalidovat více souvisejících cache položek.

Kdy cache používat

Redis cache dává smysl zejména pro:

  • lokalizace
  • sazby DPH
  • seznam států
  • seznam měn
  • konfiguraci aplikace
  • oprávnění a role
  • produktové kategorie
  • často používané agregace

Naopak není vhodná pro:

  • rychle se měnící data
  • data závislá na konkrétním uživateli
  • data s vysokými nároky na konzistenci

Proč nepoužívat Doctrine Result Cache?

Doctrine nabízí vlastní mechanismy cachování dotazů, například Query Cache nebo Result Cache.

V praxi však preferuji samostatnou cache vrstvu nad repository z několika důvodů:

  • cache logika není schovaná uvnitř Doctrine
  • jednodušší invalidace
  • lepší testovatelnost
  • možnost snadného přechodu na jiné úložiště
  • cache lze sdílet mezi různými částmi aplikace

Repository zůstává čisté a stará se pouze o databázi.

Závěr

Použití samostatné Cached Repository vrstvy nad Doctrine repository představuje jednoduchý a dobře škálovatelný způsob, jak snížit počet SQL dotazů a zrychlit odezvu Symfony aplikace.

Klíčovou výhodou je oddělení odpovědností:

  • Repository komunikuje s databází.
  • Cached Repository řeší cachování.
  • Facade obsahuje business logiku.
  • Controller pouze obsluhuje HTTP request.

Díky Redis cache lze u často čtených statických dat dosáhnout výrazného snížení zatížení databáze a rychlejších odezev aplikace bez komplikovaných změn v existující architektuře.

Buy me a coffee icon Support on Patreon

Nejnovější příspěvky