Doctrine - Entity类中的Hydrate集合

DrK*_*Key 7 php collections symfony doctrine-orm

关于OneToMany <-> ManyToOne我的实体Device和我之间的双向关系,我有一个问题Event.这是映射的外观:

// Device entity
    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Event", mappedBy="device")
     */
    protected $events;


// Event entity
    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Device", inversedBy="events")
     */
    protected $device;
Run Code Online (Sandbox Code Playgroud)

问题来自因为Device单表继承实体

 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="device_class_type", type="string")
Run Code Online (Sandbox Code Playgroud)

每次我获取并迭代某些Event实体,然后$device总是急切地获取.发生这种情况是因为它是相关文档中报告的STI实体

单表继承存在一般性能考虑因素:如果多对一或一对一关联的目标实体是STI实体,则出于性能原因,它最好是继承中的叶实体层次结构,(即没有子类).否则,Doctrine不能创建该实体的代理实例,并且总是急切地加载该实体.

现在有另一个被称为Gateway与两者有关系的实体DeviceEvent:

/**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Device", mappedBy="gateway")
 */
protected $devices;

/**
 * @ORM\OneToMany(targetEntity="targetEntity="AppBundle\Entity\Event", mappedBy="gateway")
 */
protected $events;


public function getEvents(): Collection
{
    return $this->events;
}
Run Code Online (Sandbox Code Playgroud)

当然,每次迭代$gateway->getEvents()所有相关事件时,都会急切地获取设备.即使我没有得到任何$device信息也会发生这种情况- 一个空的foreach就足以让Doctrine为每个对象执行1个查询以获取相关信息$device

foreach ($gateway->getEvents() as $event) {} 
Run Code Online (Sandbox Code Playgroud)

现在我知道我可以使用QueryBuilder设置不同的水合模式来避免$device取物

return $this->getEntityManager()->createQueryBuilder()
            ->select('e')
            ->from('AppBundle:Event', 'e')
            ->where('e.gateway = :gateway')
            ->setParameter('gateway', $gateway)
            ->getQuery()->getResult(Query::HYDRATE_SIMPLEOBJECT);
Run Code Online (Sandbox Code Playgroud)

但我想以某种方式直接在Gateway实体中做到这一点.

那么可能Gateway->events直接在Gateway实体类中进行水合作用吗?

Ton*_*cas 1

您需要编写自己的水合方法

\n\n

您有一个循环引用,其中这些节点之一(设备)将强制执行FETCH EAGER. 更糟糕的是,其中一个节点(网关)的作用类似于其他两个节点之间的多对多连接表,导致FETCH EAGER在近乎无限循环中加载所有内容(或至少是大块相关数据)。

\n\n
 +\xe2\x94\x80\xe2\x94\x80<   OneToMany\n >\xe2\x94\x80\xe2\x94\x80+   ManyToOne\n >\xe2\x94\x80\xe2\x94\x80<   ManyToMany\n +\xe2\x94\x80\xe2\x94\x80+   OneToOne\n\n       \xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80< Gateway >\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n       \xe2\x94\x82                       \xe2\x94\x82\n       +                       +\n     Event +\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80< Device*\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,当设备执行 EAGER fetch 时,它将收集 Many Gateways,therefore much Events,therefore much Devices,therefore much more Gateways,等等,Fetch EAGER直到所有引用都被填充为止。

\n\n

通过构建您自己的水合器来防止“急切”水合作用。

\n\n

构建您自己的水化器将需要一些仔细的数据操作,但对于您的用例来说可能会有些简单。请记住在 Doctrine 中注册您的水化器,并将其作为参数传递给$query->execute([], \'GatewayHydrator\');

\n\n
class GatewayHydrator extends DefaultEntityHydrator\n{\n    public function hydrateResultSet($stmt)\n    {\n        $data = $stmt->fetchAll(PDO::FETCH_ASSOC);\n        $class = $this->em->getClassMetadata(Gateway::class);\n        $gateway = $class->newInstance();\n\n        $gateway->setName($data[0][\'gateway_name\']); // example only\n\n        return $gateway;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者,删除从设备到网关的映射字段

\n\n

从 Doctrine 的角度来看,从 和中删除$gateway => Gateway映射,Device 将有效地成为一片叶子。这将避免该引用循环,但有一个缺点:必须手动设置“设备”->“网关”属性(可能在“网关”和“事件”中)DevicemappedBy="gateway"Gateway->devicesetDevice方法中)。

\n