10
votes

First off I want to say I've read through all the docs and googled this plenty before posting this question. I know what that error means (un-persisted entity in a relationship)

I'm getting this error where I think I shouldn't be getting it.

I have a OneToMany Bi-Directional relationship as follow:

Class Channel
{
    /** 
    * @ORM\OneToMany(targetEntity="Step", mappedBy="channel", cascade={"all"}, orphanRemoval=true)
    * @ORM\OrderBy({"sequence" = "ASC"})
    */
    protected $steps;
}

Class Step
{
    /** 
    * @ORM\ManyToOne(targetEntity="Channel", inversedBy="steps")
    */
    protected $channel;
}

One Channel can have many Steps and the owning side is Channel. After I upgraded from Doctrine 2.4 to 2.5 I'm getting this error:

Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship 'Company\MyBundle\Entity\Step#channel' that was not configured to cascade persist operations for entity

why is it even finding new relationships from the inverse side? Here's my code:

$channel = new Channel();
$step = new Step();
$channel->addStep($step);
$em->persist($channel);
$em->flush();

Thanks!

2
What does Channel::addStep() look like?Peter Bailey
You did not specify the join column on the Step entity. So Doctrine does not know how to save it.Fracsi
If One Channel can have many Steps then I think the owning side is Step entity, not the Channel entityPmpr

2 Answers

11
votes

You're right: Doctrine looks only for changes into owning side but you're wrong: owning side of your relationship is Step, not Channel.

Why is step the owning side? Because is the entity that has foreign key. Even Doctrine documentation says to you

The owning side has to use the inversedBy attribute of the OneToOne, ManyToOne, or ManyToMany mapping declaration. The inversedBy attribute contains the name of the association-field on the inverse-side.

Possible solutions:

  • Try to invert cascade operations by putting cascade={"all"} into Step entity (are you sure that all is the correct choice?)

  • Persist explicitly both entities:

    $channel = new Channel();
    $step = new Step();
    $channel->addStep($step);
    $em->persist($channel);
    $em->persist($step);
    $em->flush();
    

    here you can read why second way provided here is fine too

1
votes

You try to persist $channel, but it has Step entity inside. So in Doctrine now you have 2 entities that are queued for inserting. Then Doctrine order entities in the order where first is Step because it has channel_id foreign key (that is empty now). Doctrine try persist this entity and when it understands that channel_id is empty it sees for cascade rules for persisting. It doesn't see any cascade rules and throw you this exception.