Boštjan Biber
Boštjan Biber

Reputation: 502

Symfony 4.4 serializer problem when one or more items from entity's array collection are removed

I've noticed an unusual problem with Symfony 4.4 serializer which I use for entity data serialization in a controller for the rest API.

Under normal conditions, it works fine but in case if I want to serialize entity which contains property type array collection and I remove one of the array collection items without saving the entity it outputs the array collection with array containing key => value pairs instead of array of objects.

This is a quick sample:

<?php

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Serializer\SerializerInterface;

use App\Entity\SomeEntity;

class BaseController extends AbstractController
{

/**
 * @Route("/test/{testEntityId}", methods={"GET"}, name="api.v1.test")
 */
public function getTestResult(string $testEntityId, SerializerInterface $serializer)
{
    $testEntityRepository = $this->getDoctrine()->getRepository(TestEntity::class);
    $testEntity = $this->testEntityRepository->findOneBy($testEntityId);

    // ## > The code in this scope messes up with the serializer
    if ($testEntity && $testEntity->hasSomeItems()) {
        $someItem = $testEntity->getSomeItems()->getFirst();
        $testEntity->removeSomeItem($someItem);
    }

    $serialized_data = $this->serializer->serialize($entity, 'json', ['groups' => ['test']]);

    $headers = ['Content-type' => 'application/json'];
    return new Response($serialized_data, 200, $headers);
}

}

This would return

{ "someItems": [ "1": { someItem 02 object }, "2": { someItem 03 object }, etc. ] }

instead of

{ "someItems": [ { someItem 02 object }, { someItem 03 object }, etc. ] }

as it would normally.

This happens because removeSomeItem($someItem) calls method removeElement of ArrayCollection which removes item with array key 0 and the remaining items keep their keys unchanged.

I managed to get a normal response if I force the following line into Symfony's ArrayCollection removeElement method: $this->elements = array_values($this->elements); to reset the order of the array item keys.

I need a solution to fix this in a controller instead of changing the core code of the Symfony. Saving the entity with the entity manager is not applicable because I just need to remove some incompatible items for older API clients from the array collection to keep backward compatibility and don't want to persist such changes.

Any good ideas?

Thank you in advance!

Upvotes: 1

Views: 2173

Answers (2)

rugolinifr
rugolinifr

Reputation: 1281

This a well-know case, as described here.

The trick is to use Collection::getValues() within your getter.

Upvotes: 4

Boštjan Biber
Boštjan Biber

Reputation: 502

I found some related debates on GitHub and found a solution to reindex array collection items in the entity method for removing item:

<?PHP

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\SomeItem;

class SomeEntity
{
    private $someItems;


    public function __construct()
    {
        parent::__construct();
        $this->someItems = new ArrayCollection();
    }

    public function removeSomeItem(SomeItem $someItem): self
    {
        if ($this->someItems->contains($someItem)) {
            $this->someItems->removeElement($someItem);

            // Reindex array elements to avoid problems with data serialization
            $this->someItems = new ArrayCollection($this->someItems->getValues());
        }

        return $this;
    }
}

Upvotes: 1

Related Questions