Doctrine cascade update - reset child entity before persist on update

1.8k Views Asked by At

I have a Promotion Entity having a One-to-Many relationship with PromotionCategory Entities.

In the Promotion Entity I have:

/**
 * @var PromotionCategory[]
 * @OneToMany(targetEntity="AwinServices\Modules\AdvertiserPromotion\DomainLayer\Models\Promotion\Category", mappedBy="promotion", cascade={"persist", "remove"})
 */
public $promotionCategories;

And in the PromotionCategory Entity I have:

/**
 * @ManyToOne(targetEntity="AwinServices\Modules\AdvertiserPromotion\DomainLayer\Models\Promotion", inversedBy="promotionCategories")
 * @JoinColumn(name="promotionId", referencedColumnName="promotionId")
 **/
public $promotion;

The problem I am having is that every time I update a Promotion with a new Category, it keeps on creating new PromotionCategory entities for that Promotion.

What I want is to reset the previous categories for that Promotion, and create new rows for the categories I am passing.

To solve the issue currently I am trying to remove all the Categories for that promotion before persisting using a DQL DELETE statement:

$q = $this->doctrineEntityManager->createQuery("Delete from " . $this->getEntityClassname() . " r where r.promotion =" . $promotion->id);
$q->execute();

I assumed Doctrine will do that automatically as it does for ManyToMany relationships and don't understand why it doesn't do this for my case even after mentioning cascade={"persist", "remove"}

Is there a cleaner way of doing this?

1

There are 1 best solutions below

1
On

Firstly, cascade={"persist", "remove"} only applies if you persist or remove the Promotion parent of the PromotionCategory object. What you're trying to do is remove the PromotionCategory children of a Promotion object, so the cascade keyword is irrelevant in this case.

That being said, the problem with your query is that you're not joining on the Promotion entity in your query. Doctrine apparently has trouble doing this in DELETE statements, as can be seen in this SO post.

As @Danielle Suurlant says in that post, one way to do this is to fetch the PromotionCategory entities you want to remove from the database, and then just remove them with the entity manager:

$qb = $this->entityManager->createQueryBuilder();
$query = $qb->select('AwinServices\Modules\AdvertiserPromotion\DomainLayer\Models\Promotion\Category', 'category')
  ->innerJoin('category.promotion', 'promotion')
  ->where('promotion.id = :promotion_id')
  ->setParameter('promotion_id', $promotion->id)
  ->getQuery();
$results = $query->execute(); 

Then iterate over the results and remove them:

foreach ($results as $result) {
  $this->entityManager->remove($result);
}

And flush to get rid of them:

$this->entityManager->flush();

Thanks again to Danielle Suurlant for her answer to this similar problem in another post.