Я работаю над проектом на основе Symfony 2.8, который по сути представляет собой онлайн-адресную книгу.
EntityDeletionListener
используется для обработки событий postRemove
, чтобы добавить некоторую информацию в журнал удаления, который необходим для некоторого внутреннего бухгалтерского учета:
class EntityDeletionListener implements EventSubscriber {
public function getSubscribedEvents() {
return array('postRemove');
}
public function postRemove(LifecycleEventArgs $args) {
$entity = $args->getEntity();
if (!$entity->shouldAddToDeleteLog())
return ;
$em = $args->getEntityManager();
$repo = $em->getRepository('AppBundle:DeleteLog');
$log = $repo->createEntity();
$log->setGuid($entity->getGuid());
...
$em->persist($log);
// Flush entity manager if not disabled in entity settings
if ($entity->shouldFlushDeleteLog())
$em->flush();
}
}
Это работает без каких-либо проблем: каждый раз, когда объект Contact
удаляется, запись в журнале удаления создается автоматически.
Сейчас я работаю над возможностью массового удаления любого количества выбранных Contacts
.
Чтобы ускорить процесс, менеджер сущностей очищается не после каждой операции удаления, а только после обработки некоторого пакета.
class ContactRepository extends EntityRepository {
public function bulkDelete($guids) {
$this->_em->getConnection()->beginTransaction();
try {
$batchSize = 100;
$currentBatch = 0;
foreach ($guids as $guid) {
$contact = $this->findOneByGuid($guid);
if ($contact) {
// DO NOT FLUSH EVERY DELETE LOG
$contact->setDeleteLogFlush(false);
$this->_em->remove($contact);
$currentBatch++;
if ($currentBatch % $batchSize === 0)
$this->_em->flush();
}
}
$this->_em->flush();
$this->_em->getConnection()->commit();
} catch (\Exception $ex) {
$this->_em->getConnection()->rollBack();
return false;
}
}
}
Проблема 1:
Если я использую $contact->setDeleteLogFlush(false)
, чтобы избежать автоматической очистки каждого DeleteLog, DeleteLogs
не сохраняются вообще. Также объекты Contact
удаляются правильно, и postRemove
выполняется для каждой операции удаления, журналы не сохраняются в БД.
Нет никаких исключений или других ошибок, которые объясняли бы, почему журналы не сохраняются. Даже если произойдет ошибка, в этом случае полная транзакция должна завершиться неудачно, и Contacts
также не будет удален. Но они удалены ...
Проблема 2:
Если НЕ использовать $contact->setDeleteLogFlush(false)
, прослушиватель postRemove
сбрасывает каждый новый DeleteLog
, я получаю следующее исключение, как только удаляется сразу несколько записей:
Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalThrowableError: "Type error: Argument 2 passed to Doctrine\DBAL\Connection::delete() must be of the type array, null given
Как это решить? Почему журналы, созданные в прослушивателе postRemove
, не сохраняются при очистке диспетчера сущностей?
Я думаю, проблема в том, что вы хотите создать и удалить несколько объектов Contact
, но делаете это не в том случае, потому что, как указано в документация
The postRemove event occurs for an entity after the entity has been deleted. It will be invoked after the database delete operations. It is not called for a DQL DELETE statement.
и вы не должны вносить никаких изменений в базу данных как указано здесь
postUpdate, postRemove, postPersist
The three post events are called inside EntityManager#flush(). Changes in here are not relevant to the persistence in the database, but you can use these events to alter non-persistable items, like non-mapped fields, logging or even associated classes that are not directly mapped by Doctrine.
Поэтому я бы просто переместил всю логику postRemove
в событие preRemove
, поскольку
The preRemove event is called on every entity when its passed to the EntityManager#remove() method. It is cascaded for all associations that are marked as cascade delete.
Не прочитал весь вопрос, но я бы посоветовал не сохранять журналы в базе данных (а лучше не в реляционной базе данных). У вас будут проблемы, если эти объекты будут расти (в количестве), и у вас возникнут проблемы при обращении к таблице. Используйте файл или базу данных NoSQL для таких операций.