lexxpavlov
@lexxpavlov
Программист, преподаватель

Как правильно кэшировать в Doctrine данные со связями?

Использую Doctrine ORM в проекте на Symfony2, и настраиваю кэширование запросов к БД.
Настроил result_cache_driver в конфиге doctrine.orm, а также создал отдельные сервисы для кэша (использую FilesystemCache).

Для кэширования использую два подхода:
1) встроенный в ORM механизм кэширования результатов запросов:
$query = $this->em->createQuery('select a from AppBundle:Article a where a.id = :id')
    ->setParameter('id', $id)
    ->useResultCache(true, 0, $cacheKey);
$item = $query->getSingleResult();

2) Отдельное кэширование блоков:
if (($data = $cache->fetch($cacheKey)) === false) {
      $data = doHardWork();
      $cache->save($cacheKey, $data);
}

Такой подход позволяет легко настроить и получение данных из БД, и кэшировать большие вычисляемые данные (например, список похожих статей, найденных по тегам текущей статьи). Использование этих подходов позволило уменьшить до нуля кол-во запросов к БД при показе страницы. Переменная $cacheKey используется для того, чтобы в админке можно было бы сбросить кэш для редактируемой сущности (вот тут есть хорошая статья про это).

Проблема возникает в тот момент, когда на странице используется сущность со связями (ManyToOne или ManyToMany). Например, у меня есть сущность Video с полем, связанным с сущностью Media из SonataMediaBundle. Для получения данных из связанной таблицы доктрина делает отдельный запрос. Для загрузки из кэша я явно запрашиваю сделать этот запрос в контроллере:
$query = $this->em->createQuery('select v from AppBundle:Video v where v.id = :id')
    ->setParameter('id', $id)
    ->useResultCache(true, 0, $cacheKey);
$item = $query->getSingleResult();

$this->em->createQuery('select m from ApplicationSonataMediaBundle:Media m where m.id = :id')
    ->setParameter('id', $item->getVideo()->getId())
    ->useResultCache(true)
    ->getSingleResult();

Никуда присваивать результат этого запроса не требуется, Доктрина запомнит его и при запросе к видео из шаблона уже не будет делать повторный запрос.

Вопрос мой в том, можно ли как-то заставить Доктрину загружать связанные данные одним запросом?

Указывание fetch="EAGER" в аннотации поля не помогает. $query->setFetchMode тоже не помогает. Если получать нужные данные, присваивать их в загруженный объект сущности, и сохранять в отдельный кэш (во втором способе кэширования выше), то часть привязанных данных из этих дополнительных объектов почему-то теряется.
  • Вопрос задан
  • 4390 просмотров
Решения вопроса 1
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
join-ы разве что явно задать.

Вообще я не вижу смысла в вашем случае использовать кэш запросов доктрины, если у вас и так есть свой слой кеширования.

p.s. работать с QueryBuilder в контроллере не хорошо, а если бы это дело было бы в сервисе то и сделать нормально кеширование небыло бы проблем.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы