Doctrine生成大量查询以显示具有子实体的实体


Doctrine generates huge amount of queries to display entities with child entities

我有以下数据库结构(简化)。所有关系都是多对一

表父级:

ID | SOME_DATA
-------------------------------
1  | Lorum
2  | Ipsum
..etc

表子级:

ID | PARENT_ID | SOME_DATA
-------------------------------
1  | 2         | Dolor
2  | 5         | Sis
..etc

使用常规条令方法将它们全部显示在一个页面上:

<?php
//get parents
$parents = $this->getDoctrine()->getRepository('FamilyBundle:Parent')->findAll();
//loop through parents
foreach($parents AS $parent){
  //display parent
  echo '<h1>'.$parent->getName().'</h1>';
  //show children
  foreach($parent->getChildren() AS $child)
    echo '<h2>'.$child->getName().'</h2>';
}

在使用首次亮相工具时,我惊讶地发现,为了检索子实体,对每个父实体都使用了一个新的数据库查询。导致脚本效率非常低。

上面的例子被简化了。如果我不依赖实体类中的一些专门方法,我可以使用原始查询所以我的问题是,有没有一种方法可以进行更智能的查询,但仍然能够使用条令实体管理器管理数据,这样我仍然可以访问实体类方法我最好指定预加载了父实体的哪些子实体,因为我不需要使用所有子实体。

有人能给我指正确的方向吗?

如果查询中没有联接子句,则Doctrine默认使用"延迟加载",因此您必须为父实体创建一个自定义存储库类,以减少Doctrine查询数量。

只需将存储库注释添加到父实体类:

// FamilyBundle'Entity'Parent.php
namespace FamilyBundle'Entity;
use Doctrine'ORM'Mapping as ORM;
/**
 * @ORM'Entity(repositoryClass="FamilyBundle'Repository'ParentRepository")
 */
class Parent {
  protected $children; // OneToMany bidirectional annotation i suppose
  // Do not forget ArrayCollection in constructor and addChild, removeChild and getChildren methods !
}

并使用join子句创建您的自定义存储库:

// FamilyBundle'Repository'ParentRepository.php
namespace FamilyBundle'Repository;
use Doctrine'ORM'EntityRepository;
class ParentRepository extends EntityRepository
{
  public function findParents(array $criteria, array $orderBy = null)
  {
    $qb = $this
      ->createQueryBuilder('parent')
      ->leftJoin('parent.children', 'children') // join clause
      ->addSelect('children') // get children rows
    ;
    if (isset($criteria['some_data'])) // where clause example
    {
      $qb
        ->andWhere('children.some_data = :some_data') // andWhere clause works even if first where
        ->setParameter('some_data', $criteria['some_data'])
      ;
    }
    if (isset($orderBy['other_data'])) // orderBy clause example on Parent entity
    {
      $qb
        ->addOrderBy('parent.other_data', $orderBy['other_data']) // or orderBy clause
      ;
    }
    return $qb
      ->getQuery()
      ->getResult()
    ;
  }
}

在您的控制器中:

$parents = $this->getDoctrine()->getRepository('FamilyBundle:Parent')->findParents(
  array(
    'some_data' => 'dolor'
  ),
  array(
    'other_data' => 'DESC'
  )
);