Symfony2/Doctrine: получите поля, которые изменились после изменения сущности "Loggable"

В проекте Symfony2 я использую расширение Loggable Doctrine.

Я видел, что есть LoggableListener.

Действительно ли происходит событие, которое вызывается при изменении поля (регистрируемого) в регистрируемом объекте? Если это так, есть ли способ получить список полей, которые его вызвали?

Я представляю себе случай с сущностью, скажем, 10 полей, из которых 3 регистрируемых. Для каждого из 3 я хочу выполнить некоторые действия, если они меняют значение, поэтому 3 действия будут выполнены, если 3 из них изменятся.

Любая идея?

Спасибо!

РЕДАКТИРОВАТЬ

После прочтения комментария ниже и прочтения документов о событиях в доктрине я понял, что у нас есть 3 варианта:

1) использование обратных вызовов жизненного цикла непосредственно на уровне сущности, даже с аргументами, если я использую доктрину> 2.4

2) Я могу слушать и подписываться на события жизненного цикла, но в этом случае в документах говорится, что "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) делать то, что вы предлагаете, то есть использовать прослушиватель Entity, где вы можете определить на уровне сущности, кто является слушателем, который будет "присоединен" к классу.

Даже если первое решение кажется более простым, я прочитал, что "Вы также можете использовать этот прослушиватель для реализации проверки всех полей, которые изменились. Это более эффективно, чем использование обратного вызова жизненного цикла, когда требуются дорогостоящие проверки для вызова". Что считается "дорогой проверкой?".

В моем случае я должен выполнить что-то вроде "если поле X объекта Y изменилось, чем добавить уведомление в таблицу уведомлений, говорящее" пользователь Z изменил значение X(Y) с A на B"

Какой подход был бы наиболее подходящим, учитывая, что у меня около 1000 таких полей?

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 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

Любая идея?

1 ответ

Приемник Loggable только со временем сохраняет значение изменений для отслеживаемых свойств ваших объектов.

Это не вызывает событие, оно слушает onFlush а также postPersist Учение о событиях.

Я думаю, что вы ищете слушателей Doctrine для событий preUpdate и prePersist, где вы можете манипулировать набором изменений перед flush,

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

Если вы используете Doctrine 2.4+, вы можете легко добавить их в свою сущность:

Класс простой сущности:

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;
    }
}

Затем в Your\Namespace\Listener вы создаете 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)
    {
        //
    }
}

Очистите кеш и слушатель должен быть вызван при сбросе.

Обновить

Вы правы насчет RecomputeSingleEntityChangeSet, который не был необходим в этом случае. Я обновил код слушателя.

Проблема с первым выбором (методы внутри сущности) заключается в том, что вы не можете внедрить другие службы в метод. Если вам нужен только EntityManager, то да, это самый простой способ для кода.

Вы можете сделать это с помощью внешнего класса Listener.

Если эти 1000 полей находятся в нескольких отдельных объектах, второй тип слушателя будет наиболее подходящим. Вы могли бы создать NotifyOnXUpdateListener это будет содержать всю вашу логику часов / уведомлений.


Обновление 2

Чтобы внедрить службы в EntityListener, объявите прослушиватель как службу, помеченную doctrine.orm.entity_listener и введите то, что вам нужно.

<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>

и слушатель будет выглядеть так:

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)
    {
        //
    }
}

Согласно: Как использовать Doctrine Entity Listener с Symfony 2.4? это требует DoctrineBundle 1.3+

Другие вопросы по тегам