Symfony2 / Doctrine2从querybuilder对象

时间:2016-03-30 12:45:35

标签: mysql symfony doctrine-orm secondary-indexes

给定两个学说实体(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对象(或从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)。

getRootEntitygetRootAliasesgetAllAliases没有帮助,因为我获得了根实体和/或所有别名,但无法将它们链接在一起。

$em->getClassMetadata($rootentity)->associationMappings为我提供了根实体的关联,它包含连接的目标实体,但没有别名。我当然可以将字段名称映射到来自$qb->getDQLPart('join')的信息,但这会让我进入一个我必须从每个实体对象中递归递归信息的区域。这似乎可能导致严重的错误。

Querybuilder如何将关联转换为正确的实体?或者它根本不这样做,只是解析到较低级别的东西而不知道它正在使用什么实体?

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

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

2 个答案:

答案 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);

        // ...
    }
}