Arraycollection

时间:2016-09-13 20:42:08

标签: php doctrine-orm

我正在寻找一种优雅的方式来执行以下操作:

假设我有一个具有OneToMany关系的实体,例如

class Parent
{
    /**
     * @ORM\OneToMany(targetEntity="Child")
     */
    private $children;

    public function __construct()
    {
        $this->children = new ArrayCollection();
    }
}

class Child
{
    /**
     * @ORM\ManyToOne(targetEntity="Parent")
     */
    private $parent;
}

现在,在我的逻辑中,我必须过滤那些仅通过查询无法完成的父项。所以我最终得到了父母的ArrayCollection,例如:

$parents = new ArrayCollection([
    $parent1,
    $parent2,
    $parent3
]);

现在,从所有这些父母那里,我想通过一个问题加入孩子们。 我怎么能用学说做到这一点。

我知道我可以循环收集并在每个父母上调用->getChildren()

但我可能有数百个父母(这意味着数百个查询)。我想避免的。

在有人说'当你得到父母的时候对父母进行左手加入'之前,我不能仅仅通过查询来过滤我需要的父母。所以这就是为什么我不能只是LEFT JOIN。

cookies for thoughs!

2 个答案:

答案 0 :(得分:1)

为了避免N + 1 selects problem,我建议以下解决方案不需要JOIN并使用两个单独的查询。这是最有效的解决方案。

首先,检索所有父母:

$parents = $em->createQueryBuilder()
    ->select("p")
    ->from("YourFoobarBundle:Parent", "p")
    ->where(/*...*/)
    ->setParameter(/*...*/)
    ->indexBy("p.id")
    ->getQuery()->getResult();

现在加载这些父母的所有孩子:

$children = $em->createQueryBuilder()
    ->select("c")
    ->from("YourFoobarBundle:Child", "c")
    ->where("IDENTITY(child.parent) IN (?1)")
    ->setParameter(1, array_keys($parents))
    ->getQuery()->getResult();

关于Doctrine的好处是,现在所有需要的实体都存储在内存中。因此,当您执行$parent->getChildren()时,不会触发新的数据库查询(除非子项本身具有其他关系)。

注意:如果始终需要所选父母的所有孩子,则应标记$children以便加载:

/**
 * @ORM\OneToMany(targetEntity="Child", fetch="EAGER")
 */
private $children;

在这种情况下,Doctrine将始终(!)自动获取所需的孩子。

答案 1 :(得分:1)

您可以创建子查询并在其他查询的子句(表达式中的where)中使用它。它非常像@lxg描述,但您可以在一个查询中完成所有这些以提高性能(您不必单独执行查询)。

$qb  = $entityManager->createQueryBuilder();

$sub = $qb->select('p')
          ->from('Application\Entity\Parent', 'p')
          ->where(/*...*/)
          ->setParameter(/*...*/)

$children = $qb->select('c')
               ->from('Application\Entity\Child', 'c')
               ->where($qb->expr()->in('c.parent', $sub->getDQL()))
               ->getQuery()
               ->getResult();