symfony/symfony — Issue #3050: GetSetMethodNormalizer: serializing entities with cyclic references
Author: lxg
Created: 2012-01-06 16:42:47+00:00
State: open

The GetSetMethodNormalizer is not able to serialize objects with cyclic dependencies, as they occur with bidirectionally related entities. This especially happens with Doctrine Collections. As I was told on IRC the other day, there is apparently currently no solution for this, so I tried to find a workaround.

The following workaround has some serious issues with GC and architecture. Therefore, it should be merely considered a proof-of-concept hack.

The solution is two-fold: First, in order to eliminate the infinite loop, we "register" the already serialized objects in a global variable. Second, we assume that if an object has a `getValues` method, we don't loop over it's getters, but simply fill the `$attributes` variable by calling `getValues`. (This is fully BC, and we have a common convention without a too-tight coupling to Doctrine).

My PoC solution looks like this:

```php
getMethods(\ReflectionMethod::IS_PUBLIC);

if ($reflectionObject->hasMethod('getValues')) {
$attributes = $this->serializer->normalize($object->getValues(), $format);
} else {
$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = lcfirst(substr($method->getName(), 3));

$attributeValue = $method->invoke($object);

if (!is_scalar($attributeValue) && in_array($attributeValue, $objectsInList))
continue;

if (null !== $attributeValue && !is_scalar($attributeValue)) {
$attributeValue = $this->serializer->normalize($attributeValue, $format);
}

$attributes[$attributeName] = $attributeValue;
}
}
}

return $attributes;
}

private function isGetMethod(\ReflectionMethod $method)
{
return (
0 === strpos($method->getName(), 'get') &&
3 < strlen($method->getName()) &&
0 === $method->getNumberOfRequiredParameters()
);
}
}
```

As I said, this may not be the most beautiful solution, but it is powerful enough to cleanly serialize my Doctrine objects with bidirectional dependencies.

You can try on the following both (stripped PHPdoc on autogenerated methods):

```php
children = new \Doctrine\Common\Collections\ArrayCollection();
}

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

public function addTestChild(\Tixo\BackendBundle\Entity\TestChild $children)
{
$this->children[] = $children;
}

public function getChildren()
{
return $this->children;
}
}
```
and

```php
id;
}

public function setPrice($price)
{
$this->price = $price;
}

public function getPrice()
{
return $this->price;
}

public function setTest(\Tixo\BackendBundle\Entity\Test $test)
{
$this->test = $test;
}

public function getTest()
{
return $this->test;
}
}

```

Feel free to use any of this for a proper solution.