php 如何检查 Doctrine 2 中的实体是否发生了变化?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10800178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:05:17  来源:igfitidea点击:

How to check if entity changed in Doctrine 2?

phpdoctrinedoctrine-orm

提问by eagleoneraptor

I need to check if a persisted entity has changed and needs to be updated on the database. What I made (and did not work) was the following:

我需要检查持久化实体是否已更改并需要在数据库上更新。我所做的(但没有奏效)如下:

$product = $entityManager->getRepository('Product')->find(3);
$product->setName('A different name');

var_export($entityManager->getUnitOfWork()->isScheduledForUpdate($product));

That code prints always false, I also tried to flush before check the unit of work, but did not work.

该代码总是打印false,我也尝试在检查工作单元之前刷新,但没有奏效。

Anyone has a suggestion?

有人有建议吗?

采纳答案by Sergi

The first thing I'd check it that your setNamefunction is actually doing something ($this-> name = $name...) If it's already working, then you could define an event listener on your services.yml that is triggered when you call the flush.

我首先要检查您的setName函数是否确实在执行某些操作 ($this-> name = $name...) 如果它已经在工作,那么您可以在 services.yml 上定义一个事件侦听器,该侦听器在你叫同花顺。

entity.listener:
  class: YourName\YourBundle\EventListener\EntityListener
  calls:
    - [setContainer,  ["@service_container"]]
  tags:
    - { name: doctrine.event_listener, event: onFlush }

Then you define the EntityListener

然后你定义 EntityListener

namespace YourName\YourBundle\EventListener;

use Doctrine\ORM\Event;
use Symfony\Component\DependencyInjection\ContainerAware;

class EntityListener extends ContainerAware
{   

    /**
     * Gets all the entities to flush
     *
     * @param Event\OnFlushEventArgs $eventArgs Event args
     */
    public function onFlush(Event\OnFlushEventArgs $eventArgs)
    {   
        $em = $eventArgs->getEntityManager();
        $uow = $em->getUnitOfWork();

        //Insertions
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            # your code here for the inserted entities
        }

        //Updates
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            # your code here for the updated entities
        }

        //Deletions
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
            # your code here for the deleted entities
        }
    }
}

If you need to know which entities are being changed, but do something with them afterthey've been saved to the database, just store the entities changed in a private array, an then define a onFlush event that gets the entities from the array.

如果您需要知道哪些实体正在被更改,但在将它们保存到数据库后对它们进行处理,只需将更改的实体存储在私有数组中,然后定义一个从数组中获取实体的 onFlush 事件。

BTW, to trigger this kind of events you need to add the @ORM\HasLifecycleCallbacks on the entity.

顺便说一句,要触发此类事件,您需要在实体上添加 @ORM\HasLifecycleCallbacks。

回答by Pierre de LESPINAY

I didn't need/want to create Listeners for my case so I ended up with

我不需要/不想为我的案例创建监听器,所以我最终得到了

$product->setName('A different name');
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
if ($uow->isEntityScheduled($product)) {
    // My entity has changed
}

回答by Andrew Atkinson

You may also want to look at the PreUpdateevent, if you need access to entity fields with their old and new values.

如果您需要访问具有旧值和新值的实体字段,您可能还想查看PreUpdate事件。

A bit of an example mostly taken from the link provided:

主要取自提供的链接的一些示例:

<?php
class NeverAliceOnlyBobListener
{
    public function preUpdate(PreUpdateEventArgs $eventArgs)
    {
        if ($eventArgs->getEntity() instanceof User) {
            if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {
                $oldValue = $eventArgs->getOldValue('name');
                $eventArgs->setNewValue('name', 'Bob');
            }
        }
    }
}

回答by Mikl

Doctrine2 Docs. 17. Change Tracking Policies

Doctrine2 文档。17. 变更追踪政策

If you use third form (17.3. Notify) as i do, you can test if your entity is changed doing:

如果您像我一样使用第三种形式(17.3。通知),您可以测试您的实体是否已更改:

$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
$aChangeSet = $uow->getEntityChangeSet($oEntity);

If nothing changed it will return blank array.

如果没有任何改变,它将返回空白数组。

回答by Miko?aj Król

The issue is quite old but there may be still some group of people that might face this problem from a different point of view. The UnitOfWorkworks great but it only returns the array of changes. It can be a pain in butt when someone doesn't actually knows which fields may have changed and just wants to get the whole entity as an object to compare $oldEntityand $newEntity. Even though the event's name is preUpdate if someone will try to fetch the data from the database as follows:

这个问题已经很老了,但可能仍然有一些人可能会从不同的角度来面对这个问题。该UnitOfWork工程巨大,但它只返回变化的阵列。当有人实际上并不知道哪些字段可能已更改并且只想将整个实体作为对象进行比较时$oldEntity,这可能会很痛苦$newEntity。即使事件的名称是 preUpdate 如果有人会尝试从数据库中获取数据,如下所示:

$er->find($id);

the returned entity will contain all changes. The workaround is quite simple but it has some hooks:

返回的实体将包含所有更改。解决方法很简单,但它有一些钩子:

public function preUpdate(Entity $entity, PreUpdateEventArgs $args)
{
    $entity = clone $entity; //as Doctrine under the hood 
                             //uses reference to manage entities you might want 
                             //to work on the entity copy. Otherwise,        
                             //the below refresh($entity) will affect both 
                             //old and new entity.
    $em = $args->getEntityManager();
    $currentEntity = $em->getRepository('AppBundle:Entity')->find($entity->getId());
    $em->refresh($currentEntity);

}

For those who are using another event, like preFlush, I've quickly checked it and the workaround didn't work well because probably the refresh()method discards any flush changes so what needs to be done is to call the flush once again in listener and create some static $alreadyFlushedtoggle to avoid circular reference.

对于那些正在使用另一个事件的人,比如 preFlush,我已经快速检查了它并且解决方法没有很好地工作,因为该refresh()方法可能会丢弃任何刷新更改,因此需要做的是在侦听器中再次调用刷新并创建一些静态$alreadyFlushed切换以避免循环引用。

回答by Semyon Mamonov

If you only need to compare old and new state of object then probably this would be simpler:

如果您只需要比较对象的旧状态和新状态,那么这可能会更简单:

$originalEntityData = $entityManager->getUnitOfWork()->getOriginalEntityData($entityObject);

回答by Tristan

I am curious about Doctrine and everyone documenting postFlush, as in some case, you have an ongoing transaction. I'd like to point out there's also postTransactionCommit, which could be safer depending on what you're trying to achieve in the postFlush event.

我对 Doctrine 和记录 postFlush 的每个人很好奇,因为在某些情况下,你有一个正在进行的交易。我想指出还有 postTransactionCommit,它可能更安全,具体取决于您在 postFlush 事件中尝试实现的目标。

回答by rkeet

Based on my needs, answers here and the docs, I came up with the following solution for a modifiedAttimestamp in an Entity.

根据我的需要、这里答案和文档,我为modifiedAt实体中的时间戳提出了以下解决方案。

/**
 * @Doctrine\ORM\Mapping\PreUpdate()
 *
 * @param \Doctrine\ORM\Event\PreUpdateEventArgs $args
 * @return $this
 */
public function preUpdateModifiedAt(\Doctrine\ORM\Event\PreUpdateEventArgs $args)
{
    $this->setModifiedAt(new \DateTime('now'));

    return $this;
}

This is based on what the docs sayabout this Eventas opposed to the other available ones, such as PostPersistand PreFlush:

这是基于文档对此的说法Event,而不是其他可用的,例如PostPersistPreFlush

PreUpdate is the most restrictive to use event, since it is called right before an update statement is called for an entity inside the EntityManager#flush() method. Note that this event is not triggered when the computed changeset is empty.

PreUpdate 是最严格的使用事件,因为它是在为 EntityManager#flush() 方法内的实体调用更新语句之前调用的。请注意,当计算的变更集为空时,不会触发此事件。

Using PreUpdateas opposed to the others lets you leave all the computations and calculation intensive functions to the process already defined by Doctrine. Manually triggering computation of changesets, such as in theseanswersabove are server CPU intensive. The onFlush Event, such as used in the accepted answeris an option (in the way demonstrated), but not if you rely on detecting a change to the Entity, as you can with the function above (preUpdateModifiedAt(PreUpdateEventArgs $args)).

使用PreUpdate与其他方法相反,您可以将所有计算和计算密集型函数留给 Doctrine 已经定义的过程。手动触发变更集的计算,例如在上面的这些答案中是服务器 CPU 密集型的。onFlush 事件,例如在接受的答案中使用的是一个选项(以演示的方式),但如果您依赖于检测实体的更改,则不是,就像您可以使用上面的函数 ( preUpdateModifiedAt(PreUpdateEventArgs $args))。

回答by Rafael

I agree with @Andrew Atkinson when he said:

我同意 @Andrew Atkinson 说的:

You may also want to look at the PreUpdateevent, if you need access to entity fields with their old and new values.

如果您需要访问具有旧值和新值的实体字段,您可能还想查看PreUpdate事件。

But I disagree with the example he proposed, from my experience, there is a better way to check if something changed or not.

但我不同意他提出的例子,根据我的经验,有一种更好的方法来检查是否发生了变化。

<?php
class Spock
{
    public function preUpdate(PreUpdateEventArgs $eventArgs)
    {
        if (!empty($eventArgs->getEntityChangeSet())) {
            // fill this how you see fit
        }
    }
}

This way the if will only be triggered if there is really some field that changed or not.

这样 if 只会在确实有一些字段改变或没有改变时才会被触发。

As to how to do it if this or that field was changed, then yeah, I recommend his solution.

至于如果这个或那个字段改变了怎么办,那么是的,我推荐他的解决方案。