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