Doctrine2 (Doctrine 2.1) chargement rapide en Symfony2
disons que j'ai deux entités dans mon projet Symfony2 : Category
et Article
(une catégorie comportant de nombreux articles).
Dans mon CategoryRepository
, j'ai cette méthode:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.Articles a')
->getQuery()->getResult();
}
si je me souviens bien, en Symfony1.4 (et la version correspondante de la Doctrine), les objets retournés auraient leur attribut "articles" rempli par leArticle
objets.
Maintenant, dans Symfony2, les objets Proxy sont retournés.
donc si je boucle à travers une catégorie spécifique articles, autant de requêtes que d'itérations sera exécuté.
foreach($category->getArticles() as $article){
echo $article->getDoctrine()
->getRepository('')getTitle();
}
je comprends que c'est Doctrine2.1 par défaut de chargement paresseux comportement.
Question 1: comment est-ce une meilleure solution? N requêtes au lieu de 1.
j'ai essayé de forcer le chargement impatient de la façon suivante:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.articles a')
->getQuery()
->setFetchMode('Category', 'articles', 'EAGER')
->getResult();
}
Mais le résultat reste le même.
Question 2: comment forcer un chargement empressé en Doctrine2?
4 réponses
vous rejoignez une table mais vous n'en sélectionnez rien. Ajouter ->addSelect('a')
pour votre générateur de requêtes. Considérez deux requêtes SQL suivantes pour comprendre la différence:
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;
l'adhésion impatiente/paresseuse n'a rien à voir avec les requêtes DQL. Il définit ce qui doit être chargé lorsque vous utilisez $articleRepository->find(123)
.
Dans la partie où vous essayez d' "la force désireux de chargement" le problème pourrait être que vous utilisez le fetchMode
méthode avec le mauvais type de variable pour l' $fetchMode
argument. Vous passez une chaîne de caractères 'EAGER'
mais la méthode n'attend pas une chaîne mais un entier.
La méthode attend des constantes de la ClassMetadata
catégorie:
/**
* 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;
Dans la Doctrine de la documentation chapitre 14.7.6.6. Changez temporairement le mode fetch en DQL vous pouvez voir un exemple sur la façon pour utiliser ceci:
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
passez donc soit une référence à la constante soit un entier qui correspond au mode que vous voulez utiliser.
Comme il est dit dans le doctrine docs, chargement impatient dans ce cas ne fera pas de différence parce que vous avez une-à-plusieurs relation entre la catégorie et L'Article.
pour les relations un-à-plusieurs, changer le mode fetch en eager provoquera l'exécution d'une requête pour chaque entité racine chargée. Cela ne donne aucune amélioration par rapport au mode fetch paresseux qui initialisera également les associations sur une base individuelle une fois qu'elles seront accéder.
donc contrairement à ce que @Crozin a dit, Vous pouvez toujours faire des chargements rapides en DQL.
Si vous avez une relation un-à-un ou plusieurs-à-un, chargement impatient va résoudre le problème de faire des requêtes supplémentaires. Toutefois, afin de résoudre votre problème dans ce cas, vous devez utiliser ->addSelect('a')
comme @Crozin l'a mentionné.
c'est une meilleure solution car les jointures sont un processus beaucoup plus coûteux qu'une simple requête. Bien qu'il puisse sembler inefficace, il n'est pas beaucoup d'un gaspillage, et devient rapidement plus efficace lorsque vous ne chargez pas chaque peu de chaque objet connexe.