具有多个连接的 Doctrine 查询仅填充主实体

Posted

技术标签:

【中文标题】具有多个连接的 Doctrine 查询仅填充主实体【英文标题】:Doctrine Query with Multiple Joins only populating Main Entity 【发布时间】:2019-03-01 00:23:15 【问题描述】:

到目前为止,我已经尝试了以下方法,但我一直只获取主要实体信息,加入实体没有达到结果:

选项1(使用ResultSetMapping Builder):

$rsm = new ResultSetMappingBuilder(
    $this->_em,
    ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT
);
$rsm->addRootEntityFromClassMetadata(
    'CountryApp\StoreBundle\Entity\Product', 'p'
);
$rsm->addJoinedEntityFromClassMetadata(
    'CountryApp\StoreBundle\Entity\Category', 'c', 'p', 'category'
);
$rsm->addJoinedEntityFromClassMetadata(
    'CountryApp\StoreBundle\Entity\CustomerProductPrice', 'cpp', 'p', 'customerPrices'
);

$result = $this->_em
    ->createNativeQuery(
        '
    SELECT
        p.id,
        p.code,
        p.name,
        p.cost,
        p.rrp,
        p.status,
        p.notes,
        p.out_of_stock_since,
        p.available_in,
        c.id,
        c.name,
        c.code,
        cpp.id,
        cpp.price
    FROM product as p
    JOIN category as c ON c.id = p.category_id AND p.status != "DELETED"
    LEFT JOIN customer_product_price as cpp ON cpp.product_id = p.id AND cpp.customer_id = :customer
', $rsm
    )
    ->setParameter('customer', $customerId)
    ->getResult(Query::HYDRATE_ARRAY)
;

选项 2:(使用 QueryBuild 和 FetchMode)

$qb     = $this->createQueryBuilder('p');
$result = $qb
    ->select('p')
    ->addSelect('c')
    ->addSelect('cpp')
    ->join(
        'CountryApp\StoreBundle\Entity\Category',
        'c',
        Join::WITH,
        $qb->expr()
           ->eq('c', 'p.category')
    )
    ->leftJoin(
        'CountryApp\StoreBundle\Entity\CustomerProductPrice',
        'cpp',
        Join::WITH,
        $qb->expr()
           ->andX(
               $qb->expr()
                  ->eq('p', 'cpp.product'),
               $qb->expr()
                  ->eq('cpp.customer', ':customer')
           )
    )
    ->setParameter('customer', $customerId)
    ->getQuery()
    ->setFetchMode(
        'CountryApp\StoreBundle\Entity\Category', 'product', ClassMetadata::FETCH_EAGER
    )
    ->setFetchMode(
        'CountryApp\StoreBundle\Entity\CustomerProductPrice', 'product', ClassMetadata::FETCH_EAGER
    )
    ->getResult(Query::HYDRATE_ARRAY)
;

请就如何使这项工作提出您的想法。我想获得以下结构:

[
  0 => [
    Product[
      ..
    ]
    Category[
      ..
    ]
    CustomerProductPrice[
      ..
    ]
  ],
  1 => [
    Product[
      ..
    ]
    Category[
      ..
    ]
    CustomerProductPrice[
      ..
    ]
  ],
..

.
]

【问题讨论】:

您没有取回数据还是只是不符合预期的格式?因为您的映射表明,您应该得到一个 Product 并且在该对象中存在 Category 和 CustomerProductPrice。因此,连接的实体是子代而不是 Product 的兄弟姐妹。这是预期的行为。但是您可以稍后将结果列表映射到数组结构。 我只得到 Product 孩子或 Customer 和 CustomerProductPrice 没有填充。 @eiiCreative 你能解释一下你要解决的问题吗? @vytsci 我实际上是在尝试在一次 SQL 调用中返回一组结果,以避免来回跳转到服务器。在 cakephp 中,我使用包含来确保我的数据已经在结果中。看来我无法在教义中找到这样做的方法。我知道我可以更改注释以使其渴望而不是懒惰,但这会影响实体的每次使用,而不仅仅是这个用例。 哦,你来自 CakePHP :D 我会回答。 【参考方案1】:

使用 Doctrine 时,您可以定义实体内部的关系。

您可以在此处阅读更多内容https://symfony.com/doc/current/doctrine/associations.html 始终阅读文档和最佳实践。我不知道你是否使用 Symfony,但这是一个很好的例子,比 Doctrine 文档更容易理解。

/**
 * @ORM\Entity()
 */
class Product

    // ...

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
     */
    private $category;

    public function getCategory(): ?Category
    
        return $this->category;
    

    public function setCategory(?Category $category): self
    
        $this->category = $category;

        return $this;
    

正如您在此处看到的,您定义了一个包含所有关联和属性的实体。

如果您调用$product->getCategory(),默认情况下关联将被延迟加载,您的类别将被延迟加载。如果你不喜欢延迟加载,你可以随时使用

/**
 * @ManyToOne(targetEntity="Category", cascade="all", fetch="EAGER")
 */

您将收到一个产品数组,其中每个产品都有一个名为 category 的属性,并且其中包含 Category 实体。

这是 CakePHP 之间的主要区别,因为在 CakePHP 中,您分别获得所有关联,而在 Symfony 中,您获得关联树。

而且您的查询似乎太复杂了,在大多数情况下,您根本不需要修改这样的查询。但要小心延迟加载,如果您在大型列表上延迟加载数据,最终会导致性能不佳。

【讨论】:

感谢@vytsci 我最终选择了客户实体类上的 fetch EAGER 设置,因为在查询客户时我只需要 CustomerProductPrice 并且我查询客户比查询产品少得多。不过,看看 fetchMode 是否真的可以在每个查询的基础上临时更改,特别是在报告查询的情况下,会很好。不幸的是,到目前为止我的尝试没有奏效。 你可以***.com/questions/6939339/… 但是官方的教义文档不起作用,但是在 cmets 的某个地方写了语法。看看这个。 知道了,我必须明确命名我需要的字段,我会在这里很快更新谢谢@vytsci @eiiCreative np

以上是关于具有多个连接的 Doctrine 查询仅填充主实体的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine 对一对多关系进行了许多查询

具有多个连接条件的实体框架查询

Doctrine - 从反面查询一对多、单向的连接表关联

Doctrine|ORM|Symfony:可能与接口或多个实体有关系

Doctrine ORM:使用由外键组成的复合主键持久化集合

如何使 Doctrine hydrator 填充实体?