8000 Allow Denormalization of a Collection of Objets as a Constructor Argument · Issue #28475 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
Allow Denormalization of a Collection of Objets as a Constructor Argument #28475
Closed
@alsciende

Description

@alsciende

Description
The documentation for ObjectNormalizer states that "It supports calling the constructor during the denormalization process." but that is not true when one of the constructor arguments is an array of objects. Currently (since #19649), is is only possible to denormalize an "array of objects" property if that property has a setter method or is public.

Example

#!/usr/bin/env php
<?php

namespace App;

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

require __DIR__ . '/vendor/autoload.php';

class Item
{
    /**
     * @var string
     */
    private $id;

    /**
     * Item constructor.
     * @param string $id
     */
    public function __construct(string $id)
    {
        $this->id = $id;
    }

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

class Pack
{
    /**
     * @var Item[]
     */
    private $items;

    /**
     * Pack constructor.
     * @param Item[] $items
     */
    public function __construct(array $items)
    {
        $this->items = $items;
    }

    public function getItems(): array
    {
        return $this->items;
    }
}

$phpDocExtractor = new PhpDocExtractor();
$normalizer = new ObjectNormalizer(null, null, null, $phpDocExtractor);
$serializer = new Serializer([$normalizer, new ArrayDenormalizer()]);

$itemData = ["id" => "1"];
$listData = ['items' => [$itemData]];

dump($serializer->denormalize($listData, Pack::class));

Executing this script from a project root will print:

App\Pack {#20
  -items: array:1 [
    0 => array:1 [
      "id" => "1"
    ]
  ]
}

The expected result is:

App\Pack {#18
  -items: array:1 [
    0 => App\Item {#40
      -id: "1"
    }
  ]
}

That expected result can be obtained by replacing the constructor of Pack with a setter for $items:

#!/usr/bin/env php
<?php

namespace App;

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

require __DIR__ . '/vendor/autoload.php';

class Item
{
    /**
     * @var string
     */
    private $id;

    /**
     * Item constructor.
     * @param string $id
     */
    public function __construct(string $id)
    {
        $this->id = $id;
    }

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

class Pack
{
    /**
     * @var Item[]
     */
    private $items;

    public function setItems(array $items): self
    {
        $this->items = $items;

        return $this;
    }

    public function getItems(): array
    {
        return $this->items;
    }
}

$phpDocExtractor = new PhpDocExtractor();
$normalizer = new ObjectNormalizer(null, null, null, $phpDocExtractor);
$serializer = new Serializer([$normalizer, new ArrayDenormalizer()]);

$itemData = ["id" => "1"];
$listData = ['items' => [$itemData]];

dump($serializer->denormalize($listData, Pack::class));

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0