Reindexing ArrayCollection elements in RESTAPI
Asked Answered
F

1

5

Imagine, I've an array collection $items, and implemented a method removeItem in my SomeEntity entity class,

public function removeItem(Item $item)
{
    $this->items->removeElement($item);
    return $this;
}

and a controller action which receives a PATCH request:

public function patchAction(Request $request, SomeEntity $entity) {
    $form = ...;

    $form->handleRequest($request);
    if ($form->isValid()) {
        ...
        $this->get('doctrine.orm.entity_manager')->flush();

        return $entity;
    }

    return $form;
}

Imagine that the instance of the SomeEntity class holds items as [0 => {ITEM}, 1 => {ITEM}, 2 => {ITEM}, 3 => {ITEM}] (indexed array of objects). If you send a request to getAction and receive the SomeEntity object in your front project in JSON format, the type of those items in an object will be an indexed Array of objects (you can iterate over them with array methods), but if you remove one or more of them from an object with PATCH method and receive a JSON response, you get an Object type with objects in it, because some keys have been removed after the PATCH request and the object of SomeEntity class in the response no longer holds an indexed array, instead there will be an object of objects. It is because of, when you convert an array into json object you can get two different results

  1. array of objects(arrays) if keys are indexed (e.g: 0,1,2,3,...)
  2. object of objects(arrays) if keys are not indexed (e.g: 0,2,3,6,...)

At this moment I'm solving this problem with modifying (manually recreating elements) the existing removeItem method in entity class, like this:

public function removeItem(Item $item)
{
    $this->items->removeElement($item);

    if (!$this->items->isEmpty()) {
        $items = new ArrayCollection();
        foreach ($this->items as $item) {
            $items->add($item);
        }

        $this->items = $items;
    }

    return $this;
}

May be there is a better way to solve this? How do you solve this problem?

I'm using FOSRestBundle and JmsSerializerBundle.

Formosa answered 7/2, 2016 at 6:46 Comment(0)
P
10

This appears to be a common problem - see https://github.com/schmittjoh/JMSSerializerBundle/issues/373 for others having this issue. There is definitely a shorter way to solve it in your functions where you remove items than looping through each element again:

public function removeItem(Item $item)
{
    $this->items->removeElement($item);
    $this->items= new ArrayCollection($this->items->getValues());

    return $this;
}

There are other workarounds listed in that ticket that may or may not suit your needs - if you want a global solution you might be able to override the JsonSerializationVisitor class which casts to an array.

Paternoster answered 7/2, 2016 at 19:11 Comment(2)
Thanks for your help, overriding JsonSerializationVisitor visitor looks a better solution than mine.Formosa
If that works for you (from the link I provided), let me know and I'll add that answer here (or edit my question or even add your own answer and accept it)Paternoster

© 2022 - 2024 — McMap. All rights reserved.