'Doctrine 2 - (sometimes) duplicate related entities on flush
I have php worker which is executed every 60 sec. The worker go thru articles and publish them.
I have repository:
public function findAllUnpublished()
{
$query = $this->entityManager->createQuery('SELECT i FROM Extra\Item\Item i
WHERE i.status = :status')
->setParameter('status', ItemStatusEnum::UNPUBLISHED);
return $query->getResult();
}
and facade
public function publishUnpublished()
{
$items = $this->cliRepository->findAllUnpublished();
foreach ($items as $item) {
$item->setPublish(TRUE);
$this->entityManager->persist($item);
}
$this->entityManager->flush();
$this->itemChangedObserver->notifyBatchPublished($items); // this clear cache on web server
return $items;
}
and the worker where is called the facade
public function execute()
{
$this->logger->logMessage(ILogger::DEBUG, 'Start publising');
$items = $this->itemFacade->publishUnpublished();
$itemsIds = array_map(function ($item) {
return $item->getId();
}, $items);
$this->logger->logMessage(
ILogger::DEBUG,
'Published %d items (%s)',
count($items),
implode(', ', $itemsIds)
);
$this->logger->logMessage(ILogger::DEBUG, 'End publising');
return IJob::OK;
}
Problem is that sometimes(not every turn) the worker duplicate related images in gallery.
Article:
/**
* @var \Doctrine\Common\Collections\Collection
*
* @OneToMany(targetEntity="gallery", mappedBy="article", cascade={"persist", "remove"})
* @OrderBy({"position" = "ASC"})
*
*/
private $gallery;
Gallery:
/**
* @var \Article
*
* @ManyToOne(targetEntity="Article", inversedBy="gallery")
*/
private $article;
Has it ever happened to someone?
Solution 1:[1]
Calling EntityManager::persist() on an existing entry can cause side effects on associations.
So, just remove this line and keep only your flush:
$items = $this->cliRepository->findAllUnpublished();
foreach ($items as $item) {
$item->setPublish(TRUE);
}
$this->entityManager->flush();
// ...
For more, look here.
EDIT
You should use EntityManager::merge($object) rather than EntityManager::persist($object), e.g:
foreach ($items as $item) {
$item->setPublish(TRUE);
$this->entityManager->merge($item);
}
This will create a new entry if no reference can be found, otherwise this will update an existing entry.
More about merging entities.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
