Form, Fieldsets and Doctrine Entity hydration gotcha?

I’ve got a relationship between three entities that seems to be causing some remove-before-updating problems.

Setup

Here are pared-down versions of those three entities:

Step

/**
 * @ORM\Entity
 * @ORM\Table(name="steps");
 * @ORM\Entity(repositoryClass="Lemonade\Repository\StepRepository");
 */
class Step implements TimestampedEntityInterface, AdminDisplayInterface, LocalizedObjectInterface, AlpineModelProviderInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer", nullable=false, options={"unsigned"=true})
     *
     * @var int
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity="Lemonade\Entity\StepSkillReward", mappedBy="step", cascade={"all"}, orphanRemoval=true)
     *
     * @var Collection
     */
    private $skill_rewards;
}

StepSkillReward

/**
 * @ORM\Entity
 * @ORM\Table(name="steps_skills_rewards")
 */
class StepSkillReward
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Lemonade\Entity\Step", inversedBy="skill_rewards")
     * @ORM\JoinColumn(name="step_id", referencedColumnName="id", onDelete="cascade")
     *
     * @var Step
     */
    private $step;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Lemonade\Entity\Skill")
     * @ORM\JoinColumn(name="skill_id", referencedColumnName="id", onDelete="cascade")
     *
     * @var Skill
     */
    private $skill;

    /**
     * @ORM\Column(type="integer", nullable=false, options={"default":0, "unsigned":true})
     *
     * @var int
     */
    private $points_rewarded;
}

Skill

/**
 * @ORM\Entity
 * @ORM\Table(name="skills")})
 */
class Skill implements AlpineModelProviderInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer", nullable=false, options={"unsigned"=true})
     *
     * @var int
     */
    private $id;
}

Form/Hydration Issue

The problem I’m running into only occurs when you are adding and removing a sub-entity at the same time in a single update. A simple removal or a simple addition works as expected.

Focusing on the problem, when attempting to add and remove at the same time, it tries to update the old entity which causes a duplicate key problem. Let me better explain:

Imagine Step with id 800, has these StepSkillReward entities:

step, skill, points_rewarded
800, 1, 5
800, 2, 10

Then, when saving/hydrating the form, we are removing the relationship with skill 2, and are adding a new one with skill three (with points_rewarded of 5)

step, skill, points_rewarded
800, 1, 5
800, 3, 5

The expected behavior would be that Doctrine remove entity 800,2 before adding 800,3. Instead, the behavior I’m getting is a duplicate key SQL explosion like this:

UPDATE steps_skills_rewards 
SET points_rewarded = ?, skill_id = ? 
WHERE step_id = ? AND skill_id = ?
WITH PARAMS [5, 3, 800, 1]:

Paraphrasing, instead of doing:

DELETE {800, 2, 10}
INSERT {800, 3, 5}

It’s trying to do

UPDATE {800,3,5} TO {800,1,5}
INSERT {800,3,5}

But at the first operation, that entry already exists in the database.

Sorry for the eyeball scratcher - anyone seen this?

Thanks!

Hi @Saeven,

You can find your answer in this post. Thanks!

1 Like

Hey - I’ve read that man, my issue goes beyond that I think - unless I’ve missed something obvious. Here’s a repo that recreates the problem that I’ve created just now:

Hi @Saeven,

I can’t see things beyond my scope. So, I apologize that I can’t help you much further. Thanks!