Doctrine2 (Doctrine 2.1) - полная загрузка в Symfony2

Скажем, у меня есть два объекта в моем проекте Symfony2: Category и Article (категория, имеющая много статей).

В моем CategoryRepository у меня есть этот метод:

findAllDummy(){
  return $this->createQueryBuilder('c')
              ->leftJoin('c.Articles a')
              ->getQuery()->getResult();
}

Если я хорошо помню, в Symfony1.4 (и соответствующей версии Doctrine), возвращаемые объекты будут иметь свой атрибут "articles", заполненный соответствующими объектами Article. Теперь в Symfony2 возвращаются объекты Proxy.

Итак, если я прохожу через определенные статьи категории, будет выполнено столько запросов, сколько итераций.

foreach($category->getArticles() as $article){
  echo $article->getDoctrine()
               ->getRepository('')getTitle();
}

Я понимаю, что это поведение по умолчанию для Doctrine2.1 по умолчанию.

Вопрос 1: как это лучшее решение? N запросов вместо 1.

Я попытался принудительно загрузить загрузку, выполнив следующие действия:

findAllDummy(){
  return $this->createQueryBuilder('c')
              ->leftJoin('c.articles a')
              ->getQuery()
              ->setFetchMode('Category', 'articles', 'EAGER')
              ->getResult();
}

Но результат остается тем же.

Вопрос 2: как принудительно загрузить загрузку в Doctrine2?

Ответы

Ответ 1

Вы присоединяетесь к таблице, но вы ничего не выбираете из нее. Добавьте ->addSelect('a') в построитель запросов. Рассмотрим два следующих SQL-запроса, чтобы понять разницу:

SELECT a.id, a.title
FROM article a 
JOIN category c ON a.category_id = c.id 
WHERE a.id = 123;

SELECT a.id, a.title, c.id, c.name 
FROM article a 
JOIN category c ON a.category_id = c.id 
WHERE a.id = 123;

Ожидающее/ленивое соединение не имеет ничего общего с запросами DQL. Он определяет, что должно быть загружено при использовании $articleRepository->find(123).

Ответ 2

В той части, где вы пытаетесь "принудительно загрузить загрузку", проблема может заключаться в том, что вы используете метод fetchMode с неправильным типом переменной для аргумента $fetchMode. Вы передаете строку 'EAGER', но метод не ожидает строку, а целое число.

Метод ожидает константы из класса ClassMetadata:

/**
 * Specifies that an association is to be fetched when it is first accessed.
 */
const FETCH_LAZY = 2;

/**
 * Specifies that an association is to be fetched when the owner of the
 * association is fetched.
 */
const FETCH_EAGER = 3;

В главе документации Doctrine 14.7.6.6. Временно измените режим выборки в DQL, вы можете увидеть пример того, как это использовать:

$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);

Итак, передайте ссылку на константу или целое число, соответствующее режиму, который вы хотите использовать.

Ответ 3

Как говорится в доктринах docs, нетерпевая загрузка в этом случае не будет иметь никакого значения, потому что у вас есть один-ко-многим отношения между категорией и статьей.

Для отношений "один ко многим" изменение режима выборки для нетерпения приведет к выполнению одного запроса для каждого загруженного корневого объекта. Это не дает улучшения по сравнению с режимом "ленивый выбор", который также будет инициализировать ассоциации по отдельности после их доступа.

Итак, вопреки тому, что сказал @Crozin, вы все равно можете загружать в DQL. Если у вас есть отношения "один-к-одному" или "много-к-одному", нетерпевая загрузка решит проблему создания дополнительных запросов. Однако для решения вашей проблемы в этом случае вы должны использовать ->addSelect('a') как упоминалось @Crozin.

Ответ 4

Это лучшее решение, потому что соединения - гораздо более дорогой процесс, чем простой запрос. Хотя это может показаться неэффективным, это не большая часть отходов и быстро становится более эффективным, когда вы не загружаете каждый бит каждого связанного объекта.