title: Api-platform - Virtual property - Collection denormalization
body: <p>I have Reservation entity, which contains some ReservationRev(isions) collection. ReservationRev contains PersonStay collection. </p>
  
  <p>In Reservation I have virtual property personStays (getter, add, remove, setter).</p>
  
  <p>When I try to POST Reservation with personStays I get an error - denormalizer for Collection can't be found.</p>
  
  <p>class Reservation</p>
  
  <pre><code>/**
   * @var Collection
   * @Doctrine\ORM\Mapping\OneToMany(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\ReservationRev",
   *     mappedBy="container",
   *     cascade={"all"},
   *     orphanRemoval=true
   * )
   */
  protected $revisions;
  
  public function __construct(Nameable $nameable, ...) {
      $this-&gt;revisions = new ArrayCollection();
      $this-&gt;addRevision(new ReservationRev($nameable));
      ...
  }
  
  final public function addPersonStay(?PersonStay $personStay): void
  {
      if (!$this-&gt;getPersonStays()-&gt;contains($personStay)) {
          $newRevision = clone $this-&gt;getRevisionByDate();
          $newRevision-&gt;addPersonStay($personStay);
          $this-&gt;addRevision($newRevision);
      }
  }
  
  final public function getPersonStays(?\DateTime $dateTime = null): Collection
  {
      return $this-&gt;getRevisionByDate($dateTime)-&gt;getPersonStays() ?? new ArrayCollection();
  }
  
  final public function removePersonStay(?PersonStay $personStay): void
  {
      if ($this-&gt;getPersonStays()-&gt;contains($personStay)) {
          $newRevision = clone $this-&gt;getRevisionByDate();
          $newRevision-&gt;removePersonStay($personStay);
          $this-&gt;addRevision($newRevision);
      }
  }
  
  final public function setPersonStays(?Collection $personStays): void
  {
      $newRevision = clone $this-&gt;getRevisionByDate();
      $newRevision-&gt;setPersonStays($personStays ?? new ArrayCollection());
      $this-&gt;addRevision($newRevision);
  }
  </code></pre>
  
  <p>class ReservationRev</p>
  
  <pre><code>/**
   * @var Collection
   * @Doctrine\ORM\Mapping\ManyToMany(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\PersonStay",
   *     inversedBy="reservationRevisions",
   *     cascade={"all"}
   * )
   * @Doctrine\ORM\Mapping\JoinTable(name="accommodation_reservations_person_stays")
   */
  protected $personStays;
  
  /**
   * @var Reservation
   * @Doctrine\ORM\Mapping\ManyToOne(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\Reservation",
   *     inversedBy="revisions"
   * )
   * @Doctrine\ORM\Mapping\JoinColumn(name="container_id", referencedColumnName="id")
   */
  protected $container;
  
  public function __construct(Nameable $nameable, ...) {
      $this-&gt;personStays = new ArrayCollection();
      ...
  }
  
  final public function addPersonStay(?PersonStay $personStay): void
  {
      if ($personStay) {
          return;
      }
      if (!$this-&gt;personStays-&gt;contains($personStay)) {
          $this-&gt;personStays-&gt;add($personStay);
          $personStay-&gt;addReservationRevision($this);
      }
  }
  
  final public function removePersonStay(?PersonStay $personStay): void
  {
      if ($personStay) {
          return;
      }
      if ($this-&gt;personStays-&gt;removeElement($personStay)) {
          $personStay-&gt;removeReservationRevision($this);
      }
  }
  
  final public function setPersonStays(Collection $personStays): void
  {
      foreach ($this-&gt;personStays as $personStay) {
          if (!$personStays-&gt;contains($personStay)) {
              $this-&gt;removePersonStay($personStay);
          }
      }
      foreach ($personStays as $personStay) {
          if (!$this-&gt;personStays-&gt;contains($personStay)) {
              $this-&gt;addPersonStay($personStay);
          }
      }
  }
  
  /**
   * @return Collection
   */
  final public function getPersonStays(): Collection
  {
      return $this-&gt;personStays ?? new ArrayCollection();
  }
  </code></pre>
  
  <p>class PersonStay</p>
  
  <pre><code>/**
   * @var Person
   * @Doctrine\ORM\Mapping\ManyToOne(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\Person",
   *     inversedBy="personStays",
   *     cascade={"all"}
   * )
   * @Doctrine\ORM\Mapping\JoinColumn(name="person_id", referencedColumnName="id")
   */
  protected $person;
  
  /**
   * @var Collection
   * @Doctrine\ORM\Mapping\ManyToMany(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\ReservationRev",
   *     mappedBy="personStays"
   * )
   */
  protected $reservationRevisions;
  
  /**
   * @var Room
   * @Doctrine\ORM\Mapping\ManyToOne(
   *     targetEntity="Zakjakub\OswisAccommodationBundle\Entity\Room",
   *     inversedBy="personStays",
   *     cascade={"persist"}
   * )
   * @Doctrine\ORM\Mapping\JoinColumn(name="room_id", referencedColumnName="id")
   */
  protected $room;
  
  public function __construct(
      ?Person $person = null,
      ?ReservationRev $reservationRevision = null,
      ?Room $room = null,
      ...
  ) {
      $this-&gt;setPerson($person);
      $this-&gt;reservationRevisions = new ArrayCollection();
      $this-&gt;addReservationRevision($reservationRevision);
      $this-&gt;setRoom($room);
      ...
  }
  
  /**
   * @param ReservationRev|null $reservationRev
   */
  final public function addReservationRevision(?ReservationRev $reservationRev): void
  {
      if (!$reservationRev) {
          return;
      }
      if (!$this-&gt;reservationRevisions-&gt;contains($reservationRev)) {
          $this-&gt;reservationRevisions-&gt;add($reservationRev);
          $reservationRev-&gt;addPersonStay($this);
      }
  }
  
  /**
   * @return Person
   */
  final public function getPerson(): ?Person
  {
      return $this-&gt;person;
  }
  
  /**
   * Set person.
   *
   * @param Person|null $person
   */
  final public function setPerson(?Person $person): void
  {
      if (null !== $this-&gt;person) {
          $this-&gt;person-&gt;removePersonStay($this);
      }
      if ($person &amp;&amp; $this-&gt;person !== $person) {
          $person-&gt;addPersonStay($this);
          $this-&gt;person = $person;
      }
  }
  
  /**
   * @return Room|null
   */
  final public function getRoom(): ?Room
  {
      return $this-&gt;room;
  }
  
  /**
   * @param Room|null $room
   */
  final public function setRoom(?Room $room): void
  {
      if (null !== $this-&gt;room) {
          $this-&gt;room-&gt;removePersonStay($this);
      }
      if ($room &amp;&amp; $this-&gt;room !== $room) {
          $room-&gt;addPersonStay($this);
          $this-&gt;room = $room;
      }
  }
  
  /**
   * @return Collection
   */
  final public function getReservationRevisions(): Collection
  {
      return $this-&gt;reservationRevisions ?? new ArrayCollection();
  }
  
  /**
   * @param ReservationRev|null $reservationRev
   */
  final public function removeReservationRevision(?ReservationRev $reservationRev): void
  {
      if (!$reservationRev) {
          return;
      }
      if ($this-&gt;reservationRevisions-&gt;removeElement($reservationRev)) {
          $reservationRev-&gt;removePersonStay($this);
      }
  }
  </code></pre>
  
  <p>POST</p>
  
  <p><code>{
    "personStays": null (or [] or [&lt;some person stays&gt;]),
    "description": "string",
    "note": "string"
  }</code></p>
  
  <p>Error</p>
  
  <p><code>Could not denormalize object of type Doctrine\\Common\\Collections\\Collection, no supporting normalizer found.</code></p>
  
  <p>Symfony 4.2.2</p>
  
  <p>Api-platform 2.3.6</p>