php - Symfony/Doctrine: Doctrine not persisting data using Types::JSON migrating from Types::ARRAY

I work on a project that is currently using Symfony 6.1.* and

        "doctrine/annotations": "^1.0",
        "doctrine/common": "^3.1",
        "doctrine/doctrine-bundle": "^2.1",
        "doctrine/doctrine-migrations-bundle": "^3.0",
        "doctrine/orm": "^2.10",

I recently migrated from annotations to attributes, so I am now using Doctrine\DBAL\Types\Types to define types of my columns and I saw that Types::ARRAY has a deprecation-notice with the hint to use Types::JSON instead.

So for some new properties being added to an already existing entity I wanted to do exactly that.

The column-definition and get/set in my entity (Work) look like this:

    #[ORM\Column(type: Types::JSON, nullable: true)]
    private ?array $controlledKeywords = [];

    public function getControlledKeywords(): ?array
    {
        return $this->controlledKeywords;
    }

    public function setControlledKeywords(?array $controlledKeywords): self
    {
        $this->controlledKeywords = $controlledKeywords;

        return $this;
    }

Each element of this array is of type:

#[ORM\Embeddable]
class ControlledKeywordEmbeddable
{
    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $controlledKeyword;

    #[ORM\Column(type: 'boolean', nullable: false)]
    private ?bool $isWeighted;

    public function getControlledKeyword(): ?string
    {
        return $this->controlledKeyword;
    }

    public function setControlledKeyword(?string $controlledKeyword): self
    {
        $this->controlledKeyword = $controlledKeyword;

        return $this;
    }

    public function getIsWeighted(): ?bool
    {
        return $this->isWeighted;
    }

    public function setIsWeighted(?bool $isWeighted): void
    {
        $this->isWeighted = $isWeighted;
    }
}

But when I add some instances of my embeddable to this array, it is only being persisted like that:

{"1":{},"2":{}}

What I did so far was checking on my model-/norm-/viewData in my Symfony-Controller that handles this request, because I thought I might have made a mistake with a DataMapper, but the output looked absolutely fine. So next I looked into the hydrated Work-instance after the submit and validity-check to verify my impression, which also looked good. Meaning: $controlledKeywords is in fact an array of ControlledKeywordEmbeddable-instances with values as specified through my form.

Now I don't know what to look out for anymore because the next relevant thing happening in my code is already the call to

$entityManager->persist($work);
$entityManager->flush();

Using Types::ARRAY, the otherwise identical code, works perfectly fine btw. but I would like to get rid of deprecations rather earlier than later.

Hence: what am I missing?

Answer

Solution:

Found the answer to my question:

doctrine seems to use PHPs native json_encode under the hood to encode the data. But this omitts private properties, so the solution would either be to make the properties in the embeddable public (wouldn't prefer that) or to implement \JsonSerializable in the embeddable.

Source