给定两个学说实体(Person
和Company
),关联的一对多,以及一个类似于此的存储库
namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
class PersonRepository extends EntityRepository {
public function findAllByAge($age) {
$qb = $this->createQueryBuilder('p')
->select('p','c')
->leftjoin('p.company', 'c')
->where("p.age = :age")
->setParameter('age', $age);
// ...
}
}
我如何检索公司的实体(对象或名称),最好是从$ qb对象(或从Alias,DQL,AST,解析器等)中检索?
理想情况下,我希望有一个数组,其中包含Querybuilder实例使用的所有别名,或者至少包含 select 方法中定义的别名,以及它们的实体,形式如下: p>
[
'p' => 'TestBundle\Entity\Person',
'c' => 'TestBundle\Entity\Company',
// etc
]
在$qb->getDQLPart('join')
或甚至像$qb->getQuery()->getAST()->fromClause->identificationVariableDeclarations
这样的较低级别,有关于别名的连接信息,但它只包含根实体及其别名(p = TestBundle \ Entity \ Person)。
getRootEntity
,getRootAliases
,getAllAliases
没有帮助,因为我获得了根实体和/或所有别名,但无法将它们链接在一起。
$em->getClassMetadata($rootentity)->associationMappings
为我提供了根实体的关联,它包含连接的目标实体,但没有别名。我当然可以将字段名称映射到来自$qb->getDQLPart('join')
的信息,但这会让我进入一个我必须从每个实体对象中递归递归信息的区域。这似乎可能导致严重的错误。
Querybuilder如何将关联转换为正确的实体?或者它根本不这样做,只是解析到较低级别的东西而不知道它正在使用什么实体?
我需要信息,以便我可以确保某些实体字段具有特定的二级索引。可以使用注释设置这些二级索引,并通过doctrine($em->getClassMetadata($entity)->table['indexes']
)存储在实体中。
我需要(以编程方式)知道哪些字段在构建查询时具有二级索引,并且希望尽可能保持抽象树的高度。
答案 0 :(得分:1)
你应该这样做很简单:
namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
class PersonRepository extends EntityRepository {
public function findAllByAge($age) {
$qb = $this->createQueryBuilder('p')
->where("p.age = :age")
->setParameter('age', $age);
return $qb->getQuery()->getResult();
}
}
...然后,走路时回复:
$people = $personRepository->findAllByAge($age);
$companies = array_map(function ($person) {
return $person->getCompany();
}, $people);
我知道你的想法:不会产生不必要的请求吗?是不是可以在一次SQL调用中获得所有这些?嗯,这确实是可能的,但远远没有那么简单。
除非对该请求的性能有巨大需求(如大规模导入/导出),否则我不建议使用它。但是,由于Doctrine已经添加了一个抽象层,我认为这里的性能不是问题。
修改强>
那么在那种情况下,你能做的最好就是使用native queries with custom result set mappers。由于存储库的工作方式,在其中声明的常规DQL查询总是希望返回存储库的实体(在您的情况下,它将尝试吐出Person
实例),所以我担心没有真实的方式。
实际上它更好,因为DQL添加了一个在性能密集型查询中不受欢迎的抽象层。
对于长查询,我还强烈建议使用->iterate()
,它会将请求拆分为更小的块。最后,您的方法应如下所示:
namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
class PersonRepository extends EntityRepository
{
public function getAllByAgeIterator($age)
{
$rsm = new ResultSetMappingBuilder($this->_em);
// RSM configuration here
$qb = $this->_em->createNativeQuery("
// Your SQL query here
", $rsm)->setParameter('age', $age);
return $qb->getQuery()->iterate();
}
}
我没有详细说明ResultSetMappingBuilder
配置,但我认为你会发现没问题。
答案 1 :(得分:1)
如果我理解正确,您可以直接从查询中返回Company
个对象的集合,甚至可以从PersonRepository
返回:
namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
class PersonRepository extends EntityRepository {
public function findAllByAge($age) {
$qb = $this->getEntityManager()
->createQueryBuilder()
->from('YourBundle:Person', 'p')
->select('c')
->distinct()
->leftjoin('p.company', 'c')
->where("p.age = :age")
->setParameter('age', $age);
// ...
}
}
我不确定从Company
存储库返回Person
实例是否合适,但主要是约定问题。当然,您始终可以创建CompanyRepository::findAllCompaniesWithEployeesAtAge($age)
和反向连接,如:
namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;
class CompanyRepository extends EntityRepository {
public function findAllCompaniesWithEployeesAtAge($age) {
$qb = $this->createQueryBuilder('c')
->select('c')
->distinct()
->leftjoin('c.person', 'p') // or whatever inverse side is called
->where("p.age = :age")
->setParameter('age', $age);
// ...
}
}