如何使用Doctrine2中的EntityManager检索具有所有关联的实体?

时间:2011-12-07 18:00:14

标签: php doctrine-orm doctrine

我有一个简单的实体,具有多对多和一对多的关联。我知道'Joins'用于获取相关关联,这是我的问题的手动解决方案。

如何使用Doctrine2中的EntityManager获取具有所有关联的实体? e.g:

$this->em
     ->getRepository('Entities\Patientprofile')
     ->findOneByuserid('555555557')
     ->fetchAllAssociations();

5 个答案:

答案 0 :(得分:20)

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

您可以暂时设置急切获取模式:

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

如果您想动态加载与此获取模式的所有关联,您可以使用getAssociationMappings()的{​​{1}}方法,将您的实体名称作为参数传递给Doctrine\ORM\Mapping\ClassMetadataInfo的构造函数,然后以 $ assoc 的形式迭代返回的数组并调用:

ClassMetadataInfo

Doc: ClassMetadataInfo#getAssociationMappings()

答案 1 :(得分:12)

Doctrine2 setFetchMode无法使用“EAGER”

我还尝试在查询中使用setFetchMode“热切地”获取关联实体,但以下似乎不起作用:

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

当我跳进文件I found out that the third parameter $fetchMode should be an integer时。常量在Doctrine \ ORM \ Mapping:ClassMetadataInfo中定义。传递字符串时,由于this if clause,它将默认为Mapping\ClassMetadata::FETCH_LAZY

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

因此设置相应的整数解决了问题:

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

或者在顶部声明类use Doctrine\ORM\Mapping\ClassMetadata,然后使用常量:

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

编辑:

由于这里似乎有很多关于如何以正确的方式获取关联的混淆,我将编辑我的答案并添加一些关于如何使用存储库获取连接的其他信息。

According to the Doctrine documentation有两种类型的连接:

  
      
  1. 常规联接:用于限制结果和/或计算汇总值。

  2.   
  3. 获取联接:除了使用常规联接之外:用于获取相关实体并将其包含在水合结果中   查询。

  4.   

因此,要获得包含其关联的实体,您需要“获取 - 加入”所有这些关联,以确保它们被热切地加载。

我通常不使用DQL查询来获取实体并解决我的提取连接,而是将自定义方法添加到我使用查询构建器的存储库中。这比使用DQL更灵活,更易读。当我们调用createQuery方法时,查询构建器将创建正确的DQL查询。您可以检查创建的DQL查询,以便进行调试。

上述问题中Patientprofile实体存储库中此类自定义方法的示例:

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();
}

现在您可以使用自定义存储库方法通过id(例如'555555557')获取患者,包括患者文档和地址的关联:

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

确保同时使用addSelectleftJoin进行预先加载。

答案 2 :(得分:9)

Doctrine 2使用Proxy类进行延迟加载,因此在使用对象之前,实际上不需要获取关联的数据。由于Proxy类继承自您的关联类,因此您可以完全像使用fretch关联类一样使用代理。

但是,如果您确实需要获取实际的关联类,则需要告诉查询将获取模式设置为Doctrine \ ORM \ Mapping \ ClassMetadata :: FETCH_EAGER。如果您正在使用注释,则可以通过以下方式实现此目的:

e.g。

/**
 * @ManyToMany(targetEntity="Item", fetch="EAGER")
 */
private $items;

答案 3 :(得分:5)

您可以使用DQL查询:

$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();

答案 4 :(得分:0)

遇到同样的问题。 有必要拉出一个元素的所有父级链。 $query->setFetchMode(EntityClass, "alias_in_entity", 3) gets only 1 lvl deep, other parent 只是代理。 这可以通过将实体类获取模式更改为热切来解决。但是,如果由于某种原因(性能等)无法实现,则可以通过“动态”更改实体元数据来实现@wormhit

示例:

$query = $this->entityManager->createQueryBuilder()->select('fields')
            ->from(FormField::class, 'fields');
$metadata = $this->entityManager->getClassMetadata(FormField::class);
$metadata->setAssociationOverride('parent', ['fetch' => \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER]);

return $query->getOneOrNullResult();