简体   繁体   English

Symfony2 / Doctrine2从querybuilder对象获取联接的实体

[英]Symfony2/Doctrine2 get joined entities from querybuilder object

Given two doctrine entities ( Person and Company ), associated one-to-many, and a repository which looks something like this 给定两个学说实体( 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);

        // ...
    }
}

How could I retrieve the entity (object or name) of the Company, preferably from the $qb object (or from the Alias, DQL, AST, parser, etc)? 我如何才能检索公司的实体(对象或名称),最好从$ qb对象(或从Alias,DQL,AST,解析器等)中检索?

Ideally, I would like to have an array containing all the aliases used by the Querybuilder instance, or at least those defined in the select method, together with their entities, in the form: 理想情况下,我希望有一个数组,其中包含Querybuilder实例使用的所有别名,或者至少包含select方法中定义的别名以及它们的实体,形式为:

[
    'p' => 'TestBundle\Entity\Person',
    'c' => 'TestBundle\Entity\Company',
    // etc
]

In $qb->getDQLPart('join') or even something lower level like $qb->getQuery()->getAST()->fromClause->identificationVariableDeclarations there's join information regarding the aliases, but it contains only the Root Entity and its alias (p = TestBundle\\Entity\\Person). $qb->getDQLPart('join')或什至像$qb->getQuery()->getAST()->fromClause->identificationVariableDeclarations之类的较低层中,存在有关别名的$qb->getQuery()->getAST()->fromClause->identificationVariableDeclarations信息,但仅包含根实体和它的别名(p = TestBundle \\ Entity \\ Person)。

getRootEntity , getRootAliases , getAllAliases do not help as I get the root entity and/or all aliases, but no way to link them together. getRootEntitygetRootAliasesgetAllAliases对我获取根实体和/或所有别名没有帮助,但是无法将它们链接在一起。

$em->getClassMetadata($rootentity)->associationMappings gives me associations for the root entity, and it contains target entities for joins, but no aliases. $em->getClassMetadata($rootentity)->associationMappings为我提供了根实体的关联,它包含用于连接的目标实体,但没有别名。 I could map the field names to the information from $qb->getDQLPart('join') of course, but that would get me into an area where i'd have to crawl the information recursively from each entity object. 我当然可以将字段名称映射到$qb->getDQLPart('join')的信息,但这会使我进入一个区域,在该区域中我必须递归地从每个实体对象中检索信息。 That seems like it could cause serious errors. 看来这可能会导致严重的错误。

How does the Querybuilder translate the associations to the right entities? Querybuilder如何将关联转换为正确的实体? Or does it not do that at all, just parsing to the lower level stuff without ever knowing what entities it is using? 还是根本不这样做,只是解析到较低级别的内容,而不知道它在使用什么实体?

I need the info so I can ensure certain entity fields have specific secondary indexes on them. 我需要这些信息,以便确保某些实体字段具有特定的二级索引。 These secondary indexes can be set using annotations, and are stored in the entity by doctrine ( $em->getClassMetadata($entity)->table['indexes'] ). 可以使用批注设置这些二级索引,并通过原则( $em->getClassMetadata($entity)->table['indexes'] )将其存储在实体中。

I need to know (programmatically) which fields have secondary indexes while building a query, and would prefer staying as high up the abstraction tree as possible. 我需要(以编程方式)在构建查询时知道哪些字段具有次要索引,并且希望保持在抽象树上尽可能高的位置。

The way you should do this is quite simple: 您应该这样做的方法非常简单:

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();
    }
}

... then, when walking the response: ...然后,在响应时:

$people = $personRepository->findAllByAge($age);
$companies = array_map(function ($person) {
    return $person->getCompany();
}, $people);

I know what you think: wouldn't that create unnecessary requests? 我知道您的想法:不会创建不必要的请求吗? Isn't it possible to get all of that in a single SQL call? 在单个SQL调用中不可能获得所有这些信息吗? Well it is indeed possible but it is not as straightforward as this by a long shot. 好吧,确实有可能,但远没有这样简单。

I wouldn't recommand it unless there is a huge need in performance for that request (like a massive import/export). 除非对该请求的性能有巨大的需求(例如大量的导入/导出),否则我不会推荐它。 But since Doctrine already adds an abstraction layer I supposed performance was not an issue here. 但是,由于Doctrine已经添加了抽象层,因此我认为此处的性能不是问题。

EDIT: 编辑:

Well then in that case the best you could do is to use native queries with custom result set mappers . 那么,在这种情况下,您最好的办法是将本机查询与自定义结果集映射器一起使用 Because of the way repositories work, regular DQL queries declared within them will always expect to return the entity of the repository (in your case it will try to spit out a Person instance), so I'm afraid there is no real way around it. 由于存储库的工作方式,在其中声明的常规DQL查询总是希望返回存储库的实体(在您的情况下,它将尝试吐出Person实例),因此,恐怕没有真正的解决方法。

It is actually for the better since DQL adds an abstraction layer that is unwelcome in the case of performance-intensive queries. 实际上,这是更好的选择,因为DQL添加了抽象层,这在性能密集型查询中是不受欢迎的。

For long queries I also highly recommand the use of ->iterate() , which will split the request into smaller chunks. 对于长查询,我也强烈建议使用->iterate() ,它将请求拆分成较小的块。 In the end your method should look like this: 最后,您的方法应如下所示:

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();
    }
}

I did not detail the ResultSetMappingBuilder configuration but I think you will find out just fine. 我没有详细介绍ResultSetMappingBuilder配置,但我认为您会发现很好。

If I understand correctly, you can return collection of Company objects directly from query, even from PersonRepository : 如果我理解正确,则可以直接从查询(甚至从PersonRepository返回Company对象的集合:

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

        // ...
    }
}

I am not sure if returning Company instances from Person repository is good, but it is mainly convention issue. 我不确定从Person存储库返回Company实例是否很好,但这主要是约定问题。 Of course you can always create CompanyRepository::findAllCompaniesWithEployeesAtAge($age) and inverse join like: 当然,您始终可以创建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);

        // ...
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM