Я создаю новый 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"]
Знаете ли вы, как избежать этого бесполезного обновления? Благодаря!
Как сказал @ Раймонд, это ожидаемое поведение.
Жаль, но если ваше слияние не зависит от свойства 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 сравнивает объекты по ссылке (hash) с ===
, также новый экземпляр \DateTime
вызывает обновление, даже если дата объекта не изменяется.
Это очень проблематичная проблема, которая может быть исправлена с помощью ==
качестве оператора сравнения, но доктрина не может создать определенное условие для \DateTime
не нарушая их общий механизм сравнения объектов, что связано с уменьшением производительности одной из наиболее используемых функций ,
По-видимому, это похоже на ошибку в Доктрине, которая до сих пор не решена (Reference Github )
Это ожидаемое поведение, объекты сравниваются по ссылке