简体   繁体   中英

Doctrine update model with oneToMany fields

Couldn't get much help from the World Wide Wide Wide Web ...

The situation is:

  • My model has two manyToMany fields and one oneToMany field.
  • When I update the model, doctrine magically remove the old values for manyToMany fields and set the new one,
  • but for the oneToMany field it does not remove the old value but add the new one.

Is this a normal behaviour?

To work around I am DQLing to empty the oneToMany field target Entity for the mapped by model id and persisting new values

ie Model: Rule has

/**
* @OneToMany(targetEntity="\Tier", mappedBy="rule", cascade={"persist", "remove"})
* @JoinColumn(name="ruleId", referencedColumnName="ruleId")
* @var Tier[]
*/
public $tiers;

And in the Rule Mapper I am deleting all tiers before calling PERSIST to get UPDATE working:

public function resetTiers($ruleId)
{
$modelClass = "Tier";

$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();

return $numDeleted;
}

I don't mind doing this way as long as its OK and not introducing any bad practice.

Thank you for your time.

Please read this bit of Doctrine's documentation .

Doctrine will only check the owning side of an association for changes.

And:

OneToMany is always the inverse side of a bidirectional association.

Although:

You can pick the owning side of a many-to-many association yourself.

So yes, it's normal behavior.

My guess is your ManyToMany relations are owned by your model (ie the Doctrine declaration you put in your property's comment says inversedBy and not mappedBy ). Which would be why they're automatically updated (as per the doc linked above).

So either you start working on the other side's entities (namely Tier ), or you trick the ORM by updating the other side's entities directly through your Rule 's accessors, probably something like:

public function setTiers($tiers) {
    foreach ($this->tiers as $tier) {
        $tier->setRule(null);
    }
    foreach ($tiers as $tier) {
        $tier->setRule($this);
    }
    if (is_array($tiers)) {
        //this wrapper is useful if you use syntaxes like
        //$someRule->getTiers()->add($someTier); which is usual if you use
        //an addTier(Tier $tier) method declared in Rule
        $tiers = new \Doctrine\Common\Collections\ArrayCollection($tiers);
    }
    $this->tiers = $tiers;
}

For the first option, working on other side's entities, just calling $someTier->setRule($someRule) will auto-update* $someRule 's $tiers .

*: auto-update should happen 1/ when you flush your mods and 2/ maybe you'll need to refresh the $someRule object (I mean the EM's refresh method).

All of this is "my guess", anyone feel free to correct, and OP feel free to give some feedback about how it went if you try this! ;)

Here is some more doc about updating related entities, especially at the bottom of the page, section 6.14.1.

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM