Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API Platform: PUT with cascade option for join table

I'm using Symfony 5 with API Platform and Doctrine. I have a "Source" entity and a "SourceContributor" join table, which contains additional info for the relation.

Source:

/**
 * ...
 *
 * @ORM\Entity()
 */
class Source
{
    // ...

    /**
     * @Groups({"source:read", "source:write"})
     * @ORM\OneToMany(targetEntity="SourceContributor",  mappedBy="source", cascade={"persist", "merge"}, orphanRemoval=true)
     */
    private Collection $contributors;

    public function getContributors(): Collection
    {
        return $this->contributors;
    }

    public function addContributor(SourceContributor $contributor): self
    {
        if (!$this->contributors->contains($contributor)) {
            $contributor->setSource($this);
            $this->contributors[] = $contributor;
        }

        return $this;
    }

    public function removeContributor(SourceContributor $contributor): self
    {
        if ($this->contributors->contains($contributor)) {
            $this->contributors->removeElement($contributor);
        }

        return $this;
    }

    public function __construct()
    {
        $this->contributors = new ArrayCollection();
    }
}

SourceContributor:

/**
 * @ORM\Entity
 */
class SourceContributor
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\ManyToOne(targetEntity="Source", inversedBy="contributors")
     */
    private ?Source $source;

    public function getSource(): ?Source
    {
        return $this->source;
    }

    public function setSource(?Source $source): self
    {
        $this->source = $source;
        return $this;
    }


    /**
     * @Groups({"source:read", "source:write"})
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\ManyToOne(targetEntity="Contributor")
     */
    private ?Contributor $contributor;

    public function getContributor(): ?Contributor
    {
        return $this->contributor;
    }

    public function setContributor(?Contributor $contributor): self
    {
        $this->contributor = $contributor;
        return $this;
    }


    /**
     * @Groups({"source:read", "source:write"})
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\Column(type="smallint", nullable=false, options={"unsigned"=true})
     */
    private ?int $role;

    public function getRole(): ?int
    {
        return $this->role;
    }

    public function setRole(int $role): self
    {
        $this->role = $role;
        return $this;
    }
}

Creating my Source entity via POST like this works fine:

{
  "contributors": [
    {
      "contributor": "/contributors/1",
      "role": 0
    },
    {
      "contributor": "/contributors/1",
      "role": 1
    }
  ]
}

However, after the entity is created, updating it the same way via PUT does not work.

An exception occurred while executing 'INSERT INTO source_contributor (role, source_id, contributor_id) VALUES (?, ?, ?)' with params [0, 4, 1]:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '4-1-0' for key 'PRIMARY'

Doctrine seems to be trying to insert the SourceContributor again. Does anyone know a solution?

like image 584
Somebody Avatar asked Feb 05 '26 19:02

Somebody


1 Answers

It happens because you pass an object, if you don't need to create a new source_contributor, you have to pass a URI. Replace

{
  "contributors": [
    {
      "contributor": "/contributors/1",
      "role": 0
    }
  ]
}

to:

{
  "contributors": [
    "/contributors/1"
  ]
}
like image 112
Kate Syrotchuk Avatar answered Feb 09 '26 09:02

Kate Syrotchuk