Слияние Doctrine: всегда обновляется поле DateTime

Я создаю новый Entity с существующим Id, и я хочу обновить соответствующую запись базы данных.

Doctrine merge был моим лучшим другом: узнает, есть ли изменения и генерирует правильный запрос на обновление.

$entity = new Entity();
$entity->setId(1);
$entity->setName('test');
$EntityManager->merge($entity);
$EntityManager->flush();

Предположим, что элемент с id = 1 уже существует в db: если имя отличается от "test", Doctrine генерирует этот запрос:

UPDATE table SET name = ? WHERE id = ? ["test","1"]

Если я снова запустил код выше, Doctrine узнает, что ничего не изменилось, и запрос не передан.

Но..., когда я устанавливаю поле DateTime, Doctrine считает его измененным и всегда запускает запрос на обновление:

$entity = new Entity();
$entity->setId(1);
$entity->setDate(new \DateTime(2000-01-01));
$EntityManager->merge($entity);
$EntityManager->flush();
//* ALWAYS commits this query:
>> UPDATE table SET date = ? WHERE id = ? ["2000-01-01 00:00:00","1"]

Знаете ли вы, как избежать этого бесполезного обновления? Спасибо!

Ответ 1

Как сказал @Раймонд, это ожидаемое поведение.

Жаль, но если ваше слияние не зависит от свойства date, обходным путем может быть установка даты после слияния следующим образом:

$entity = new Entity();
$entity->setId(1);
// Set all fields that the merge is depending on
// ...
$EntityManager->merge($entity);

$entity->setDate(new \DateTime('2000-01-01'));
$EntityManager->flush();

Обновление

После проверки единственная альтернатива, похоже, извлекает объединенный объект и обновляет его, снова очищая EntityManager.

Вы можете использовать:

$entity = new Entity();
$entity->setId(1);
// Set all fields that the merge is depending on
$EntityManager->merge($entity);
$EntityManager->flush();

$entity = $EntityManager->getRepository('Your\Entity')->find(1); // Retrieve the entity
$entity->setDate(new \DateTime('2000-01-01'));
$EntityManager->flush(); // Reflush

Update2

Более чистый способ, который я нашел для достижения обновления после слияния, повторно объединяет объект, например.

$entity = new Entity();
$entity->setId(1);
// Set all fields that the merge is depending on
$EntityManager->merge($entity); // The real merge that retrieve (without commit) or create
$EntityManager->flush();

$entity->setDate(new \DateTime('2000-01-01'));
$entityManager->merge($entity); // Remerge the object with the changed field
$EntityManager->flush(); // Working re-flush

Но это не меняет основной проблемы и на самом деле не имеет смысла, потому что вы не можете сравнивать объект DateTime самостоятельно, $entity->getDate() всегда возвращайте null перед вызовом setDate даже после первого слияния.

Doctrine сравнивает объекты по ссылке (хеш) с ===, также новый экземпляр \DateTime вызывает обновление, даже если дата объекта не изменяется.

Это действительно проблематичная проблема, которая может быть исправлена ​​с помощью == в качестве оператора сравнения, но доктрина не может создать специфическое условие для \DateTime, не нарушая их общий механизм сравнения объектов, что связано с уменьшением производительности одного наиболее часто используемой функции.

Ответ 2

По-видимому, это ошибка в доктрине, которая до сих пор не разрешена (ссылка Github)

Это ожидаемое поведение, объекты сравниваются по ссылке