Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve an entity with all of its associations using EntityManager in Doctrine2?

I have a simple entity with many-to-many and one-to-many associations. I'm aware of 'Joins' for fetching related associations which is a manual solution for my problem.

How can I fetch an entity with all of its associations using EntityManager in Doctrine2? e.g.:

$this->em
     ->getRepository('Entities\Patientprofile')
     ->findOneByuserid('555555557')
     ->fetchAllAssociations();
like image 253
Mehdi Fanai Avatar asked Dec 07 '11 18:12

Mehdi Fanai


4 Answers

from http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql

you can set eager fetch mode temporarily:

$query = $em->createQuery("SELECT u FROM MyProject\User u");
$query->setFetchMode("MyProject\User", "address", "EAGER");
$query->execute();

If you want do load dynamically all associations with this fetch mode, you can use the getAssociationMappings() method of the Doctrine\ORM\Mapping\ClassMetadataInfo, passing your entity name as parameter to the constructor of ClassMetadataInfo and then iterate over the returned array as $assoc and call:

$query->setFetchMode("MyProject\User", $assoc, "EAGER");

Doc: ClassMetadataInfo#getAssociationMappings()

like image 175
timaschew Avatar answered Nov 15 '22 00:11

timaschew


Doctrine2 setFetchMode not working with "EAGER"

I tried also to fetch the associating entities "eagerly" using setFetchMode in my query, but the following didn't seem to work:

$query->setFetchMode("MyProject\User", "address", "EAGER");

When I jumped into the files I found out that the third parameter $fetchMode should be an integer. The constants are defined in Doctrine\ORM\Mapping:ClassMetadataInfo. When passing a string it will default to Mapping\ClassMetadata::FETCH_LAZY because of this if clause.

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

/**
 * Specifies that an association is to be fetched lazy (on first access) and that
 * commands such as Collection#count, Collection#slice are issued directly against
 * the database if the collection is not yet initialized.
 */
const FETCH_EXTRA_LAZY = 4;

So setting the corresponding integer solved the problem:

$query->setFetchMode("MyProject\User", "address", 3);

Or declare the class use Doctrine\ORM\Mapping\ClassMetadata at the top and then use the constant:

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

EDIT:

Since there seems to be a lot of confusion here on how to fetch associations the right way I will edit my answer and add some additional information on how you can fetch join using your repository.

According to the Doctrine documentation there are 2 types of joins:

  1. Regular Joins: Used to limit the results and/or compute aggregate values.

  2. Fetch Joins: In addition to the uses of regular joins: Used to fetch related entities and include them in the hydrated result of a query.

So to get an entity including its associations you will need to "fetch-join" all these associations to make sure they are loaded eagerly.

I usually don't use DQL queries for getting entities and solving my fetch joins, instead I add a custom method to a repository where I use a query builder. This is more flexible and much more readable then using DQL. The correct DQL query will be created by the query builder when we call the createQuery method. You can check the created DQL query of course for debug purposes.

An example for such a custom method inside the Patientprofile entity repository from the question above:

public function findPatientByIdWithAssociations($id)(
    // create a query builder for patient with alias 'p'
    $qb = $this->createQueryBuilder('p')
               ->where('p.id = :patient_id')
               ->addSelect('pd')
               ->leftJoin('p.documentation', 'pd')
               ->addSelect('pa')
               ->leftJoin('p.address', 'pa')
               ->setParameter('patient_id', $id);

    $query = $queryBuilder->getQuery();
    return $query->getSingleResult();
}

And now you can use your custom repository method to get the patient by id (for example '555555557') including associations to the patient documentation and address:

$repository = $this->em->getRepository('Entities\Patientprofile');
$patient = $repository->findPatientByIdWithAssociations('555555557');

Make sure you use both addSelect and leftJoin to do eager loading.

like image 17
Wilt Avatar answered Nov 15 '22 00:11

Wilt


Doctrine 2 uses Proxy classes for lazy loading, so you don't actually need to have the associations' data fetched until you use the objects. Since the Proxy classes inherit from your association classes, you're able to use the proxies exactly as you would use the fretch association classes.

but, if you really need to fetch the actual association classes, you need to tell the query to set the fetch mode to Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER. If you're using the annotations, you can achieve this with:

e.g.

/**
 * @ManyToMany(targetEntity="Item", fetch="EAGER")
 */
private $items;
like image 8
chriswoodford Avatar answered Nov 15 '22 00:11

chriswoodford


You can use a DQL query:

$query = $em->createQuery("SELECT p, f FROM Entities\\Patientprofile p JOIN p.Foo f WHERE p.id = ?1");
$query->setParameter(1, 321);
$patient = $query->getSingleResult();
like image 5
Maxence Avatar answered Nov 15 '22 01:11

Maxence