Doctrine2 (Doctrine 2.1) eager loading in Symfony2

15,508

Solution 1

You're joining a table but you're not selecting anything from it. Add ->addSelect('a') to your query builder. Consider two following SQL queries to understand the difference:

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;

Eager/lazy joining has nothing to do with DQL queries. It defines what should be loaded when you use $articleRepository->find(123).

Solution 2

In the part where you try to "force eager loading" the problem might be that you use the fetchMode method with the wrong variable type for the $fetchMode argument. You pass a string 'EAGER' but the method doesn't expect a string but an integer.

The method expects constants from the ClassMetadata class:

/**
 * 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;

In the Doctrine documentation chapter 14.7.6.6. Temporarily change fetch mode in DQL you can see an example on how to use this:

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

So pass either a reference to the constant or an integer that corresponds to the mode you want to use.

Share:
15,508

Related videos on Youtube

Guillaume Flandre
Author by

Guillaume Flandre

Parisian web developer guillaumeflandre.com/tech twitter.com/gflandre

Updated on June 04, 2022

Comments

  • Guillaume Flandre
    Guillaume Flandre almost 2 years

    Let's say I have two entities in my Symfony2 project : Category and Article (a category having many articles).

    In my CategoryRepository, I have this method:

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

    If I remember well, in Symfony1.4 (and the corresponding version of Doctrine), the returned objects would have their 'articles' attribute filled by the corresponding Article objects. Now, in Symfony2, Proxy objects are returned.

    So if I loop through a specific category's articles, As many queries as iterations will be executed.

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

    I understand this is Doctrine2.1's default lazy loading behavior.

    Question 1: how is this a better solution? N queries instead of 1.

    I tried to force eager loading by doing the following:

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

    But the result remains the same.

    Question 2: how to force eager loading in Doctrine2?

  • Sam Selikoff
    Sam Selikoff almost 11 years
    @Crozin I'm still a little confused...when you say "it defines what should be loaded", are you referring to what is hydrated?
  • Crozin
    Crozin almost 11 years
    @SamSelikoff Yes, if you define fetch=EAGER on articles' relation, all articles will be loaded (and hydrated) when you call categoryRepository->findOne(321)
  • Sam Selikoff
    Sam Selikoff almost 11 years
    What's the difference between loading and hydrating?
  • Crozin
    Crozin almost 11 years
    @SamSelikoff There is non in this context. Treat these two words as synonyms.
  • Sam Selikoff
    Sam Selikoff almost 11 years
    Hmm. So either of the two queries in your answer could be eagerly or lazily loaded?
  • Crozin
    Crozin almost 11 years
    @SamSelikoff Yes, you can decide whether you want to load something eagerly or lazily in both, DQL and the EntityManager::find*(). You just need to use different "tools": ->addSelect(...) in DQL and fetch="EAGER" in em->find().
  • Ryall
    Ryall over 9 years
    @Crozin It should be fetch="EAGER" (with quotes).
  • Sithu
    Sithu over 8 years
    If we have join and select, we don't need setFetchMode()
  • DrKey
    DrKey almost 7 years
    you saved my day :)