如何将 Doctrine ORM 默认跟踪策略更改为延迟显式

Posted

技术标签:

【中文标题】如何将 Doctrine ORM 默认跟踪策略更改为延迟显式【英文标题】:How to change Doctrine ORM default tracking policy to Deferred Explicit 【发布时间】:2017-01-04 11:56:52 【问题描述】:

我正在为我的最新项目使用带有 symfony 的 Doctrine ORM。我需要我的原则默认使用延迟显式跟踪策略 - 我需要控制保存的内容和时间。

我可以在其注释中更改每个实体的学说跟踪政策

/**
* @ORM\ChangeTrackingPolicy("DEFERRED_EXPLICIT")
*/

但我想将此跟踪策略设为所有实体的默认值。我不想仅仅因为这个而创建一个共同的父实体。有没有办法为整个项目设置此跟踪策略默认值?

关于教义tracking policies。

为您节省时间

【问题讨论】:

【参考方案1】:

有一个Doctrine\ORM\Events::loadClassMetadata 事件,您可以挂钩并创建一个侦听器来覆盖实体元数据中的策略。

在元数据从 annotations/yml/xml 加载后调用监听器,然后保存在缓存中,所以应该很有效。

Events: Lifecycle Events Events: Load ClassMetadata Event Symfony: How to Register Event Listeners and Subscribers

这是一些工作代码:

class DoctrineTrackingPolicySubscriber implements EventSubscriber

    public function getSubscribedEvents()
    
        return [
            Events::loadClassMetadata
        ];
    

    public function loadClassMetadata(LoadClassMetadataEventArgs $args)
    
        $classMetadata = $args->getClassMetadata();
        $classMetadata->setChangeTrackingPolicy(
            ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT
        );
    

然后在services.yamlservices.yml

App\EventSubscriber\DoctrineTrackingPolicySubscriber:
    tags:
        -  name: doctrine.event_subscriber, connection: default 

对于 Symfony 版本 class 是必要的,较新的版本隐式假定服务名称是 FQCN。

【讨论】:

这意味着您将动态更改每个类的实体跟踪策略(但不是针对每个实例)。这是可以接受的解决方案,我会给它几天,如果有人发现更明显的东西(比如教义设置)。无论如何谢谢你 - Díky Filipe :) 请注意,使用 Load ClassMetadata 事件将让您覆盖任何实体的指定更改跟踪策略,但当实体未另行指定时,更改默认策略将不起作用。 最好使用监听器而不是订阅者,因为订阅者必须始终被实例化,即使没有使用,而监听器仅在实际需要时才被实例化(谈到教义订阅者,symfony 订阅者的行为不同)。跨度> @Rikudou_Sennin Symfony 最近引入了对惰性学说订阅者的支持 @FilipProcházka 在任何地方都找不到,想分享一个链接吗?【参考方案2】:

您可以通过定义自己的工厂来构造 \Doctrine\ORM\Mapping\ClassMetadata,将默认的更改跟踪策略从 Deferred Implicit 更改为 Deferred Explicit

此方法仍然允许您为您的配置指定任何一个,否则只会影响您的配置未指定的地方。

<?php

class MyClassMetadataFactory extends Doctrine\ORM\Mapping\ClassMetadataFactory

    /**
     * @inheritDoc
     */
    protected function newClassMetadataInstance($className)
    
        $classMetadata = parent::newClassMetadataInstance($className);

        // Change the default Change Tracking Policy to Deferred Explicit.
        $classMetadata->setChangeTrackingPolicy(
            Doctrine\ORM\Mapping\ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT
        );

        return $classMetadata;
    


$xmlMetadataConfig = \Doctrine\ORM\Tools\Setup::createXMLMetadataConfiguration([__DIR__ . '/config']);

// Set your class to be the factory for ClassMetadata.
$xmlMetadataConfig->setClassMetadataFactoryName(MyClassMetadataFactory::class);

$em = Doctrine\ORM\EntityManager::create(
    ['pdo' => $pdo],
    $xmlMetadataConfig
);

该策略的关键要素如下。

覆盖\Doctrine\ORM\Mapping\ClassMetadataFactory::newClassMetadataInstance() 以更改\Doctrine\ORM\Mapping\ClassMetadata 的构造实例的属性。 将您的类设置为要在元数据驱动程序配置中使用的工厂。

【讨论】:

这需要覆盖不打算被覆盖且对性能极其敏感的类。使用监听器可以实现同样的效果,而且更简洁,因为监听器是一个扩展点。 @FilipProcházka - 连接到事件侦听器并不能更改实体的 默认 策略(如果我错了,请证明。)我也是当 Doctrine 在构造实体管理器时将其作为配置选项提供时,该类不应该被覆盖的论点毫无疑问。 您在技术上是正确的(在此处插入技术上正确的模因),但听众实现了相同的结果和我的主要论点 - 更干净,因为它是专用的扩展点。该类可以被覆盖,但恕我直言,应尽可能避免。【参考方案3】:

我当前的解决方案只是比在项目范围内更改跟踪策略更烦人 - 我必须始终将所有更改的实体传递给 EntityManager->flush($entity=null)。换句话说 - 我必须防止在没有参数的情况下调用 flush 方法。

【讨论】:

以上是关于如何将 Doctrine ORM 默认跟踪策略更改为延迟显式的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine 2.0 准备好使用了吗?

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

如何使用 Doctrine 更改列字符集和排序规则?

ORM 中的跟踪数据如何保持最新?

Doctrine ORM - 如何保持静态属性

使用Doctrine2加入表中的鉴别器