Symfony2 / Doctrine:获取在“Loggable”之后变化的字段实体更改 [英] Symfony2/Doctrine: Get the field(s) that changed after "Loggable" entity changed

查看:148
本文介绍了Symfony2 / Doctrine:获取在“Loggable”之后变化的字段实体更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我在一个Symfony2项目中使用了Loggable Doctrine扩展。我看到有一个LoggableListener。



确实有一个事件当loggable实体中的(可登录)字段发生变化时会被触发?如果是这样,有没有办法得到触发它的字段列表?



我想象一个实体的情况,让我们说10个字段其中3个可登录。对于3中的每一个,如果它们更改值,我想执行一些操作,因此如果3个操作更改,将执行3个操作。



任何想法?



谢谢!



编辑



在阅读下面的评论并阅读关于教义事件的文档之后,我理解有3个选项:



1)使用生命周期回调,甚至 2.4

$ b $,那么这个参数就会被修改为:* .readthedocs.org / en / latest / reference / events.html#lifecycle-callbacks-event-argumentrel =nofollow b

2)我可以听并订阅生命周期事件,但在这种情况下,文档说生命周期事件是触发为所有实体。听众和订阅者有责任检查该实体是否属于要处理的类型。



3)做什么建议您使用实体侦听器,您可以在实体级别定义将要附加到该类的侦听器。



即使第一个解决方案似乎更容易,我读到你也可以使用这个监听器来实现所有改变的字段的验证。这比使用生命周期回调更有效率,当有昂贵的验证调用。什么被认为是昂贵的验证?。



在我的情况下,我必须执行是类似如果实体Y的字段X改变而不是在通知表上添加通知,表示用户Z将X(Y)的值从A更改为B





EDIT2



为了解决我的问题,我试图在监听器中注入service_container服务,以便我可以访问调度程序来调度一个可以执行我需要的新实体持久化的新事件。但是我该如何做呢?



我尝试了通常的方式,我将以下内容添加到service.yml

  app_bundle.project_tolereances_listener:
class:AppBundle\EventListener\ProjectTolerancesListener
arguments:[@service_container]

当然,我将以下内容添加到监听器中:

  protected $ container; 

public function __construct(ContainerInterface $ container)
{
$ this-> container = $ container;
}

但我得到以下内容:

  Catchable致命错误:参数1传递给AppBundle\ProjectEntityListener\ProjectTolerancesListener :: __ construct()必须是AppBundle\ProjectEntityListener\ContainerInterface的实例,在第73行调用D:\provarepos\user\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\DefaultEntityListenerResolver.php并定义

任何想法?

解决方案

监听器只会保存您实体随时间观看的属性的更改值。



它不会触发一个事件,它监听 onFlush postPersist doctrine事件。



我认为您正在PreUpdate和prePersist事件上寻找Doctrine监听器,您可以在之前操纵变更集



请参阅: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html



如果您使用的是Doctrine 2.4+,您可以将它们轻松添加到您的实体中:



简单实体类:

 命名空间Your\Namespace\Entity; 

使用Doctrine\ORM\Mapping作为ORM;

/ **
* @ ORM\Entity
* @ ORM\EntityListeners({Your\Namespace\Listener\DogListener})
* /
class Dog
{
/ **
* @ ORM\Id
* @ ORM\GeneratedValue(strategy =IDENTITY)
* @ ORM\Column(type =integer)
* /
private $ id;

/ **
* @ ORM\Column(type =string,length = 100)
* /
private $ name;

/ **
* @ ORM\Column(type =integer)
* /
private $ age;

/ **
* @return int
* /
public function getId()
{
return $ this-> id ;
}

/ **
* @param int $ id
* /
public function setId($ id)
{
$ this-> id = $ id;
}

/ **
* @return string
* /
public function getName()
{
return $这 - >名称;
}

/ **
* @param string $ name
* /
public function setName($ name)
{
$ this-> name = $ name;
}

/ **
* @return int
* /
public function getAge()
{
return $这 - >年龄;
}

/ **
* @param int $ age
* /
public function setAge($ age)
{
$ this-> age = $ age;
}
}

然后在 Your\ namespace\Listener 创建ListenerClass DogListener

  namespace Your\Namespace\Listener; 

使用Doctrine\ORM\Event\LifecycleEventArgs;
使用Doctrine\ORM\Event\PreUpdateEventArgs;
使用你的\Namespace\Entity\Dog;

class DogListener
{
public function preUpdate(Dog $ dog,PreUpdateEventArgs $ event)
{
if($ event-> hasChangedField name')){
$ updatedName = $ event-> getNewValue('name')。 狗
$ dog-> setName($ updatedName);
}

if($ event-> hasChangedField('age')){
$ updatedAge = $ event-> getNewValue('age')%2;
$ dog-> setAge($ updatedAge);
}

}

public function prePersist(Dog $ dog,LifecycleEventArgs $ event)
{
//
}
}

清除缓存时,刷新时应调用侦听器。



更新



您在这种情况下不需要重新计算SingleEntityChangeSet的对象。我更新了监听器的代码。



第一个选择(实体内方法)的问题是您无法在方法中注入其他服务。
如果你只需要EntityManager,那么是的,这是代码最简单的方法。



使用外部Listener类,你可以这样做。 p>

如果这1000个字段在几个单独的实体中,则第二种类型的监听器将是最适合的。您可以创建一个 NotifyOnXUpdateListener ,其中包含所有的观看/通知逻辑。






更新2



要在EntityListener中注入服务,声明Listener作为标记为的服务doctrine.orm.entity_listener 并注入你需要的东西。

 < service id =app.entity_listener.your_serviceclass =Your\\ \\Namespace\Listener\SomeEntityListener> 
< argument type =serviceid =logger/>
<参数type =serviceid =event_dispatcher/>
< tag name =doctrine.orm.entity_listener/>
< / service>

,监听器将如下所示:

  class SomeEntityListener 
{
private $ logger;
private $ dispatcher;

public function __construct(LoggerInterface $ logger,EventDispatcherInterface $ dispatcher)
{
$ this-> logger = $ logger;
$ this-> dispatcher = $ dispatcher;
}

public function preUpdate(Block $ block,PreUpdateEventArgs $ event)
{
//
}
}

根据:如何使用Symfony 2.4的Doctrine Entity Listener?它需要DoctrineBundle 1.3 +


In a Symfony2 project I'm using the Loggable Doctrine Extension.

I saw that there is a LoggableListener.

Is there indeed an event that gets fired when a (loggable) field in a loggable entity changes? If it is so, is there a way to get the list of fields that triggered it?

I'm imagining the case of an entity with, let's say 10 fields of which 3 loggable. For each of the 3 I want to perform some actions if they change value, so 3 actions will be performed if the 3 of them change.

Any idea?

Thank you!

EDIT

After reading the comment below and reading the docs on doctrine's events I understood have 3 options:

1) using lifecycle callbacks directly at the entity level even with arguments if I'm using doctrine >2.4

2) I can listen and subscribe to Lifecycle Events, but in this case the docs say that "Lifecycle events are triggered for all entities. It is the responsibility of the listeners and subscribers to check if the entity is of a type it wants to handle."

3) doing what you suggest, which is using an Entity listener, where you can define at the entity level which is the listener that is going to be "attached" to the class.

Even if the first solution seems easier, I read that "You could also use this listener to implement validation of all the fields that have changed. This is more efficient than using a lifecycle callback when there are expensive validations to call". What's considered an "expensive validation?".

In my case what I have to perform is something like "if field X of entity Y changed than add a notification on the notification table saying "user Z changed the value of X(Y) from A to B"

Which would be the most suitable approach, considering that I have around 1000 fields like those?

EDIT2

To solve my problem I'm trying to inject the service_container service inside the listener, so that I can have access to the dispatcher to dispatch a new event which can perform the persist of new entity I need. But how can I do that?

I tried the usual way, I add the following to the service.yml

app_bundle.project_tolereances_listener:
    class: AppBundle\EventListener\ProjectTolerancesListener
    arguments: [@service_container] 

and of course I added the following to the listener:

protected $container;

public function __construct(ContainerInterface $container)
{
    $this->container = $container;
}

but I get the following:

Catchable Fatal Error: Argument 1 passed to AppBundle\ProjectEntityListener\ProjectTolerancesListener::__construct() must be an instance of AppBundle\ProjectEntityListener\ContainerInterface, none given, called in D:\provarepos\user\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\DefaultEntityListenerResolver.php on line 73 and defined

Any idea?

解决方案

The Loggable listener only saves the changesvalue for the watched properties of your entities over time.

It does not fire an event, it listens to the onFlush and postPersist doctrine events.

I think you are looking for Doctrine listeners on preUpdate and prePersist events where you can manipulate the changeset before a flush.

see: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html

If you are using Doctrine 2.4+ you can add them easily to your entity:

Simple entity class:

namespace Your\Namespace\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 *  @ORM\Entity
 *  @ORM\EntityListeners({"Your\Namespace\Listener\DogListener"})
 */
class Dog
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;

    /**
     * @ORM\Column(type="integer")
     */
    private $age;

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param int $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return int
     */
    public function getAge()
    {
        return $this->age;
    }

    /**
     * @param int $age
     */
    public function setAge($age)
    {
        $this->age = $age;
    }
}

Then in Your\Namespace\Listener you create the ListenerClass DogListener:

namespace Your\Namespace\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Your\Namespace\Entity\Dog;

class DogListener
{
    public function preUpdate(Dog $dog, PreUpdateEventArgs $event)
    {         
        if ($event->hasChangedField('name')) {                
            $updatedName = $event->getNewValue('name'). ' the dog';
            $dog->setName($updatedName);         
        }

        if ($event->hasChangedField('age')) {
            $updatedAge = $event->getNewValue('age') % 2;
            $dog->setAge($updatedAge);
        }

    }

    public function prePersist(Dog $dog, LifecycleEventArgs $event)
    {
        //
    }
}

Clear the cache and the listener should be called when flushing.

Update

You are right about recomputeSingleEntityChangeSet which was not needed in this case. I updated the code of the listener.

The problem with the first choice (in-entity methods) is that you can't inject other services in the method. If you only need the EntityManager then yes, it is the easiest way code-wise.

With an external Listener class, you can do so.

If those 1000 fields are in several separate entities, the second type of Listener would be the most suited. You could create a NotifyOnXUpdateListener that would contain all your watch/notification logic.


Update 2

To inject services in an EntityListener declare the Listener as a service tagged with doctrine.orm.entity_listener and inject what you need.

<service id="app.entity_listener.your_service" class="Your\Namespace\Listener\SomeEntityListener">
        <argument type="service" id="logger" />
        <argument type="service" id="event_dispatcher" />
        <tag name="doctrine.orm.entity_listener" />
</service>

and the listener will look like:

class SomeEntityListener
{
    private $logger;
    private $dispatcher;

    public function __construct(LoggerInterface $logger, EventDispatcherInterface $dispatcher)
    {
        $this->logger = $logger;
        $this->dispatcher = $dispatcher;
    }

    public function preUpdate(Block $block, PreUpdateEventArgs $event)
    {
        //
    }
}

According to: How to use Doctrine Entity Listener with Symfony 2.4? it requires DoctrineBundle 1.3+

这篇关于Symfony2 / Doctrine:获取在“Loggable”之后变化的字段实体更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆