Javier Trejo
Javier Trejo

Reputation: 1

Api Platform not persist OneToMany relation

I'm working with API Platform using Doctrine Inheritance and I have a problem persisting a OneToMany relation.

curl --location 'http://localhost/api/dummies' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "fakeField": "string",
  "metadata": [
    {
      "name": "string",
      "value": "string"
    }
  ]
}'

The response return the metadata as empty array and the objects was not persisted on the table.

{
  "id": "21f2051e-4dbe-4992-976d-ba3421c6aece",
  "fakeField": "string",
  "metadata": []
}

The entities definitions:

// ./src/Entity/AbstractMetadataAwareEntity.php

#[ORM\Entity]
#[ORM\Table(name: 'an_objects')]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'entity_type', type: 'string')]
#[ORM\DiscriminatorMap([
    'user' => User::class,
    'dummy' => Dummy::class,
])]
class AbstractMetadataAwareEntity
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: UuidGenerator::class)]
    #[ORM\Column(type: 'uuid', unique: true)]
    #[Groups(['base:read'])]
    protected UuidV6 $id;

    #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Metadata::class, cascade: ['persist'])]
    #[Groups(['base:read', 'base:write'])]
    protected Collection $metadata;

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

    public function getId(): UuidV6
    {
        return $this->id;
    }

    /**
     * @return Collection<int, Metadata>
     */
    public function getMetadata(): Collection
    {
        return $this->metadata;
    }

    public function addMetadata(Metadata $metadata): self
    {
        if (!$this->metadata->contains($metadata)) {
            $this->metadata->add($metadata);
            $metadata->setOwner($this);
        }

        return $this;
    }

    public function removeMetadata(Metadata $metadata): self
    {
        if ($this->metadata->removeElement($metadata)) {
            // set the owning side to null (unless already changed)
            if ($metadata->getOwner() === $this) {
                $metadata->setOwner(null);
            }
        }

        return $this;
    }
}
// ./src/Entity/Dummy.php

#[ORM\Entity(repositoryClass: DummyRepository::class)]
#[ApiResource(
    normalizationContext: [
        'groups' => ['base:read', 'dummy:read']
    ],
    denormalizationContext: [
        'groups' => ['base:write', 'dummy:write']
    ]
)]
class Dummy extends AbstractMetadataAwareEntity
{
    #[ORM\Column(length: 255)]
    #[Groups(['dummy:read', 'dummy:write'])]
    private ?string $fakeField = null;

    public function getFakeField(): ?string
    {
        return $this->fakeField;
    }

    public function setFakeField(string $fakeField): self
    {
        $this->fakeField = $fakeField;

        return $this;
    }
}
// ./src/Entity/Metadata.php

#[ORM\Entity(repositoryClass: MetadataRepository::class)]
#[ORM\Table(name: 'an_metadatas')]
class Metadata
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write'])]
    private ?string $name = null;

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write'])]
    private ?string $value = null;

    #[ORM\ManyToOne(inversedBy: 'metadata')]
    #[ORM\JoinColumn(nullable: false)]
    private ?AbstractMetadataAwareEntity $owner = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(string $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function getOwner(): ?AbstractMetadataAwareEntity
    {
        return $this->owner;
    }

    public function setOwner(?AbstractMetadataAwareEntity $owner): self
    {
        $this->owner = $owner;

        return $this;
    }
}

This implementation use Doctrine Inheritance

The expected result is the correct persistance for User and Metadata objects into their respective tables

Upvotes: 0

Views: 508

Answers (1)

Arleigh Hix
Arleigh Hix

Reputation: 10897

Given the following from Dummy:

    #[Groups(['dummy:read', 'dummy:write'])]
    private ?string $fakeField = null;

And the fact that the response is showing "fakeField" I deduce that you have the group 'dummy:read' or 'dummy:write' however the collection items do not use either of those groups:

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write'])]
    private ?string $name = null;

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write'])]
    private ?string $value = null;

So there are no property values to include in the serialized data. Try adding the 'dummy:*' groups to each field you want returned.

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write', 'dummy:read', 'dummy:write'])]
    private ?string $name = null;

    #[ORM\Column(length: 255)]
    #[Groups(['base:read', 'base:write', 'dummy:read', 'dummy:write'])]
    private ?string $value = null;

Sorry, I'm not an API Platform guy.

Upvotes: 0

Related Questions