3

给定两个学说实体(PersonCompany),一对多关联,以及一个看起来像这样的存储库

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 对象(或从别名、DQL、AST、解析器等)中检索?

理想情况下,我希望有一个数组,其中包含 Querybuilder 实例使用的所有别名,或者至少是那些在select方法中定义的别名及其实体,格式如下:

[
    '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 如何将关联转换为正确的实体?或者它根本不这样做,只是解析到较低级别的东西而不知道它正在使用什么实体?

我需要这些信息,以便确保某些实体字段上有特定的二级索引。这些二级索引可以使用注解来设置,并通过学说($em->getClassMetadata($entity)->table['indexes'])存储在实体中。

在构建查询时,我需要(以编程方式)知道哪些字段具有二级索引,并且希望尽可能保持在抽象树的较高位置。

4

2 回答 2

2

你应该这样做的方法很简单:

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 已经添加了一个抽象层,我认为性能在这里不是问题。

编辑:

那么在这种情况下,您可以做的最好的事情就是使用带有自定义结果集映射器的本机查询。由于存储库的工作方式,在其中声明的常规 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配置,但我想你会发现就好了。

于 2016-03-30T13:17:01.400 回答
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);

        // ...
    }
}
于 2016-03-30T14:12:40.453 回答