Метод UnitOfWork->getOriginalEntityData возвращает новую сущности при связи OneToOne(всегда) и ManyToOne(при изменении связанной сущности)
Предисловие: столкнулся сразу с двумя проблемами связанными с получением данных сущностей/оригинальных данных в жизненном цикле Doctrine, а точнее в событии PreUpdate. Использовать фичи вроде EntityManager->detach и EntityManager->refrsh не могу, т.к. контекст вопроса - написание логгера для уже функционирующего приложения, эти же методы вызовут коллизии в дальнейшем и сложно контролируемое поведение.
Класс уже написан и вне слушателей жизненного цикла Doctrine работает для первой проблемы, вторая сохраняется.
Описание и вводный код:
1. Первая проблема.
Имеется две сущности, можно представить как User и Address, соответственно пользователь ссылается на свой уникальный адрес и имеют следующую связь:
// UserEntity
@ORM\OneToOne(targetEntity=Address::class, mappedBy="user", cascade={"persist", "remove"})
// AddressEntity
@ORM\OneToOne(targetEntity=User::class, inversedBy="address", cascade={"persist", "remove"})
Далее, я отслеживаю изменения с помощью прослушивать события для сущности User.
App\Event\Listeners\User\UserChangedListener:
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'preUpdate'
entity: 'App\Entity\User\User'
method: 'UserChanged'
lazy: true
Код прослушивателя:
public function userChanged(User $user)
{
$campaignChanges = $this->getChangesOfEntityService->getEntityChanges($user);
// далее, код нас не интересует, т.к. проблема в предыдущей строке
}
Код сервиса getChangesOfEntityService:
public function getEntityChanges($entity)
{
$originalEntityData = $this->em->getUnitOfWork()->getOriginalEntityData($entity);
// что-то происходит, но это не важно, т.к. интересует предыдущая строка
var_dump($originalEntityData); // все нижеследующее будет относиться к этому дампу
}
В итоге, получаю следующее поведение:
- Вношу изменения в User, изменяю связанную сущность с
AddressEntity(ID:1)наAddressEntity(ID:2). - В дампе
var_dump($originalEntityData);имеется ключaddressи ожидается, что в нем будет сущностьAddressEntity(ID:1), т.к. в комментариях к методуgetOriginalEntityDataсказано, что этот метод получает данные как на момент извлечения их из хранилища. - В действительности же, в дампе
var_dump($originalEntityData);уже лежит новая сущность, т.е. нет возможности получить оригинальную и сравнить, была ли она изменена.
2. Вторая проблема.
Имеется аналогично две сущности, однако связь уже ManyToOne от User к Location например.
// UserEntity
@ORM\ManyToOne(targetEntity=Location::class, mappedBy="user", cascade={"persist", "remove"})
// AddressEntity
@ORM\ManyToOne(targetEntity=User::class, inversedBy="location", cascade={"persist", "remove"})
Далее, код аналогичен первой проблеме.
Получаем такое поведение:
- Меняем сущность Location через setter для сущности User (
user->setLocation(location(ID:2))), ранее былаlocation(ID:1). - В дампе
var_dump($originalEntityData);, под ключемlocationбудет все как положено, сущностьlocation(ID:1). в. Однако, если дополнительно изменить саму сущностьlocation, допустим такlocation->setName('changedLocationName')и повторить все действия, то в дампеvar_dump($originalEntityData);будет новая сущность сID=2, а не как в пункте б сID=1. Соответственно ситуация похожая первой проблеме, когда черезoriginalEntityDataневозможно получить исходное значение свойства.
Собственно вопросы:
- С чем связанно данное поведение? (тут разумеется понятно, что с рекалькуляцией изменений самой Doctrin-ой и вызовом этих методов в слушателях ее жизненного цикла, но можно явно самому пересчитать эти изменения и результат будет совершенно иной (правильный, сказано в предисловии)).
- Как поправить, если возможно? (интересен конечный результат, но для понимая конечно бы пролить свет на эту загадку).