title: API-Platform - How to update and embedded relation and persist it's id's
body: <p>I have two entities,</p>
  <p><code>Quote</code> and <code>QuoteProduct</code> with a <code>OneToMany</code> Relation between.</p>
  <p>When I perform a PUT request to update the embedded QuoteProduct relation, it always remove all items and add them, which result to generate new id's.</p>
  <p>According to the official doc, <a href="https://api-platform.com/docs/core/serialization/#denormalization" rel="nofollow noreferrer">https://api-platform.com/docs/core/serialization/#denormalization</a>
  I should be able to update a specific item by passing his id</p>
  <p><strong>Quote Entity</strong></p>
  <pre><code>&lt;?php
  
  namespace App\Entity;
  
  use Doctrine\Common\Collections\ArrayCollection;
  use Doctrine\Common\Collections\Collection;
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Validator\Constraints as Assert;
  use Ramsey\Uuid\Uuid;
  use ApiPlatform\Core\Annotation\ApiResource;
  use Gedmo\Mapping\Annotation as Gedmo;
  use Symfony\Component\Serializer\Annotation\Groups;
  
  
  /**
   * @ApiResource(
   *     normalizationContext={&quot;groups&quot;={&quot;quote.read&quot;}},
   *     denormalizationContext={&quot;groups&quot;={&quot;quote.write&quot;}},
   *     collectionOperations={
   *         &quot;get&quot;={&quot;access_control&quot;=&quot;is_granted('ROLE_USER')&quot;, &quot;access_control_message&quot;=&quot;You do not have the permission to get&quot;},
   *         &quot;post&quot;={&quot;access_control&quot;=&quot;is_granted('ROLE_COMMERCIAL')&quot;, &quot;access_control_message&quot;=&quot;You do not have the permission to add&quot;}
   *     },
   *     itemOperations={
   *         &quot;get&quot;,
   *         &quot;put&quot;={&quot;access_control&quot;=&quot;is_granted('ROLE_COMMERCIAL')&quot;, &quot;access_control_message&quot;=&quot;You do not have the permission to update&quot;},
   *         &quot;delete&quot;={&quot;access_control&quot;=&quot;is_granted('ROLE_COMMERCIAL')&quot;, &quot;access_control_message&quot;=&quot;You do not have the permission to delete.&quot;}
   *     },
   *     attributes={&quot;force_eager&quot;=false}
   * )
   * @ORM\Entity
   * @ORM\Table(name=&quot;`quotes`&quot;)
   */
  class Quote
  {
  
  
      public function __construct()
      {
          $this-&gt;id = Uuid::uuid4();
          $this-&gt;products = new ArrayCollection();
      }
  
  
      /**
       *
       * @ORM\Id
       * @ORM\Column(type=&quot;uuid&quot;, unique=true)
       */
      private $id;
  
  
  
      /**
       * @var Collection|QuoteProduct[]
       *
       * @Groups({&quot;quote.read&quot;, &quot;quote.write&quot;})
       * @ORM\OneToMany(targetEntity=&quot;App\Entity\QuoteProduct&quot;, mappedBy=&quot;quote&quot;, cascade={&quot;persist&quot;}, orphanRemoval=true)
       * @ORM\OrderBy({&quot;code&quot; = &quot;ASC&quot;})
       */
      private $products;
  
  
  
  
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ----------------------------------------GETTER AND SETTER------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
  
  
      public function getId()
      {
          return $this-&gt;id;
      }
  
  
      /**
       * @return Collection|QuoteProduct[]
       */
      public function getProducts(): Collection
      {
          return $this-&gt;products;
      }
  
      /**
       * @param QuoteProduct $product
       * @return Quote
       */
      public function addProduct(QuoteProduct $product): self
      {
          if(false === $this-&gt;products-&gt;contains($product))
          {
              $product-&gt;setQuote($this);
              $this-&gt;products-&gt;add($product);
          }
          return $this;
      }
  
      /**
       * @param QuoteProduct $product
       * @return Quote
       */
      public function removeProduct(QuoteProduct $product)
      {
          if(!$this-&gt;products-&gt;contains($product))
          {
              return;
          }
          $this-&gt;products-&gt;removeElement($product);
          $product-&gt;setQuote(null);
      }
  
  
  
  }
  
  ?&gt;
  </code></pre>
  <p><strong>QuoteProduct Entity</strong></p>
  <pre><code>&lt;?php
  
  
  namespace App\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Validator\Constraints as Assert;
  use Ramsey\Uuid\Uuid;
  use ApiPlatform\Core\Annotation\ApiResource;
  use Gedmo\Mapping\Annotation as Gedmo;
  use Symfony\Component\Serializer\Annotation\Groups;
  
  
  /**
   * @ApiResource(
   *     normalizationContext={&quot;groups&quot;={&quot;quote-product.read&quot;},&quot;enable_max_depth&quot;=true},
   *     denormalizationContext={&quot;groups&quot;={&quot;quote-product.write&quot;},&quot;enable_max_depth&quot;=true},
   *     attributes={&quot;force_eager&quot;=false}
   * )
   * @ORM\Entity
   * @ORM\Table(name=&quot;`quote_products`&quot;)
   */
  class QuoteProduct
  {
  
      /**
       *
       * @ORM\Id
       * @ORM\Column(type=&quot;uuid&quot;, unique=true)
       */
      private $id;
  
      /**
       * @ORM\Column(type=&quot;string&quot;, length=255, nullable=true)
       * @Groups({&quot;quote-product.read&quot;, &quot;quote-product.write&quot;, &quot;quote.read&quot;, &quot;quote.write&quot;})
       *
       */
      private $code;
  
  
      /**
       * @ORM\ManyToOne(targetEntity=&quot;Quote&quot;, cascade={&quot;persist&quot;}, inversedBy=&quot;products&quot;)
       * @Assert\Valid
       * @Groups({&quot;quote-product.read&quot;, &quot;quote-product.write&quot;, &quot;quote.read&quot;, &quot;quote.write&quot;})
       */
      private $quote;
  
  
  
  
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ----------------------------------------GETTER AND SETTER------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
      /* ---------------------------------------------------------------------------------------------- */
  
  
      public function __construct()
      {
          $this-&gt;id = Uuid::uuid4();
      }
  
      public function getId()
      {
          return $this-&gt;id;
      }
  
  
  
  
  
      
      public function getQuote(): ?Quote
      {
          return $this-&gt;quote;
      }
  
      public function setQuote(?Quote $quote)
      {
          $this-&gt;quote = $quote;
  
          return $this;
      }
  
  
     
  
      public function getCode(): ?string
      {
          return $this-&gt;code;
      }
  
      public function setCode(?string $code): self
      {
          $this-&gt;code = $code;
  
          return $this;
      }
  
      
  }
  </code></pre>
  <p>Get request to quotes/fbe01952-2a23-4882-bb9f-970e8598f979</p>
  <p>Result:</p>
  <pre><code>{
    &quot;@context&quot;: &quot;\/contexts\/Quote&quot;,
    &quot;@id&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;,
    &quot;@type&quot;: &quot;Quote&quot;,
    &quot;products&quot;: [
      {
        &quot;@id&quot;: &quot;\/quote_products\/15216edd-dd5c-49d5-bf3f-39d389d804d6&quot;,
        &quot;@type&quot;: &quot;QuoteProduct&quot;,
        &quot;code&quot;: &quot;Code 1&quot;,
        &quot;quote&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;
      }
    ]
  }
  </code></pre>
  <p>Put request to quotes/fbe01952-2a23-4882-bb9f-970e8598f979
  with JSON params (please note that I changed the value of QuoteProduct code property)</p>
  <pre><code>{
    &quot;@context&quot;: &quot;\/contexts\/Quote&quot;,
    &quot;@id&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;,
    &quot;@type&quot;: &quot;Quote&quot;,
    &quot;products&quot;: [
      {
        &quot;@id&quot;: &quot;\/quote_products\/15216edd-dd5c-49d5-bf3f-39d389d804d6&quot;,
        &quot;@type&quot;: &quot;QuoteProduct&quot;,
        &quot;code&quot;: &quot;A new code&quot;,
        &quot;quote&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;
      }
    ]
  }
  </code></pre>
  <p>Resulting to :</p>
  <pre><code>{
    &quot;@context&quot;: &quot;\/contexts\/Quote&quot;,
    &quot;@id&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;,
    &quot;@type&quot;: &quot;Quote&quot;,
    &quot;products&quot;: [
      {
        &quot;@id&quot;: &quot;\/quote_products\/865da321-dd74-4a39-82a3-e0e935555262&quot;,
        &quot;@type&quot;: &quot;QuoteProduct&quot;,
        &quot;code&quot;: &quot;A new code&quot;,
        &quot;quote&quot;: &quot;\/quotes\/fbe01952-2a23-4882-bb9f-970e8598f979&quot;
      }
    ]
  }
  </code></pre>
  <p>The code has changed has expected but it generated a new @id.</p>