0

I have entities Campaign and Creative with relations One Campaign has Many Creatives

In Campaign:

/**
 * @ORM\OneToMany(targetEntity="Creative", mappedBy="campaign", cascade={"detach", "persist"})
 */
protected $creatives;

In Creative:

/**
 * @ORM\ManyToOne(targetEntity="Campaign", inversedBy="creatives", cascade={"detach", "persist"})
 * @ORM\JoinColumn(name="campaign_id", referencedColumnName="id")
 */
private $campaign;

In frontend, I have multiple select where I can add/ remove creatives, and it sends an array of creative ids to the backend.

Problem is how to detach Creative from Campaign when I removing one from select.

I did not found any solution what doctrine can propose, so I tried:

$campaign->getCreatives()->clear();
foreach (json_decode($data['creatives'],true) as $creativeId) {
    $campaign->addCreative($this->_em->getReference(Creative::class, $creativeId));
}

but looks like it's not working, code removing the element from Campaign object but not changing campaign_id to null for related creative in the database. Maybe I need also detach it from Creative side, but it's extra work to do, I thought that cascade={"detach"} should handle it.

I would be grateful for any help!!!

FYI: I'm using doctrine with Laravel.

Updated I created sync method in Campaign as a workaround:

public function syncCreatives(array $creatives)
{
    //detach all creatives
    if(empty($creatives)) {
        /** @var Creative $creative */
        foreach ($this->creatives->getIterator() as $creative) {
            $creative->setCampaign(null);
        }
        $this->creatives->clear();

        return true;
    }
    //get nonexistent creatives
    $creativesToRemove = $this->creatives->filter(function (Creative $creative) use (&$creatives) {
        if(isset($creatives[$creative->getId()])) {
            unset($creatives[$creative->getId()]);

            return false;
        }

        return true;
    });
    //remove nonexistent creatives
    foreach ($creativesToRemove->getIterator() as $creative) {
        $this->removeCreative($creative);
    }
    //add new creatives
    foreach ($creatives as $creative) {
        $this->addCreative($creative);
    }
}

related methodds:

 /**
 * @param Creative $creative
 */
public function removeCreative(Creative $creative)
{
    if($this->creatives->contains($creative)) {
        $creative->setCampaign(null);
        $this->creatives->removeElement($creative);
    }
}

/**
 * @param Creative $creative
 */
public function addCreative(Creative $creative)
{
    if(!$this->creatives->contains($creative)) {
        $this->creatives->add($creative);
        $creative->setCampaign($this);
    }
}

So now I'm updating Campaign and Creative objects but still, it's not detaching creatives from campaign

Updated. I found this article Doctrine: How to unset (SET NULL) OneToMany relationship but I do not see what the difference between my code and solution? I should nnot use persist? But in my logic I update alot of Campaign fields and I need to persist campaign before flush

Bogdan Dubyk
  • 4,756
  • 7
  • 30
  • 67
  • Does this help? https://stackoverflow.com/questions/8858645/doctrine-2-onetomany-cascade-set-null – Jannes Botis Feb 16 '18 at 20:09
  • Thanks, it's not exactly what I want, I do not want to delete campaign, I want just to detach creative from it, but let me try and I'll let you know if it works – Bogdan Dubyk Feb 16 '18 at 20:19

1 Answers1

0

Okay, I found what was the ussie. It was my fault :(

In Cretive I had code

/**
 * @param Campaign $campaign
 */
public function setCampaign(Campaign $campaign = null)
{
    if(!is_null($campaign)) {
        $this->campaign = $campaign;
        $this->campaign->addCreative($this);
    }

}

I do not remember when and why I add this if(!is_null($campaign)).

Removing it and start working

Bogdan Dubyk
  • 4,756
  • 7
  • 30
  • 67