I am building a JSON-based REST API using Symfony2.4 with Doctrine2.
EDIT : with JsonNormalizer, I can disabled some attributes, but what if I would like to set them, without recursivity ?
Basically, what I have (working) now is :
{
"tasks": [
{
"id": 1,
"name": "first task"
},
{
"id": 2,
"name": "second task"
}
]
}
What I would like is :
{
"tasks": [
{
"id": 1,
"name": "first task",
"category": {
"id": 1,
"name": "first category"
}
},
{
"id": 2,
"name": "second task",
"category": {
"id": 1,
"name": "first category"
}
}
]
}
What was my initial problem is :
{
"tasks": [
{
"id": 1,
"name": "first task",
"category": {
"id": 1,
"name": "first category",
"tasks": [
{
"id": 1,
"name": "first task",
"category": {
"id": 1,
"name": "first category",
"tasks": [...] // infinite...
}
},
{
"id": 2,
"name": "second task",
"category": {
"id": 1,
"name": "first category",
"tasks": [...] // infinite...
}
}
]
}
},
{
"id": 2,
"name": "second task",
"category": {
"id": 1,
"name": "first category",
"tasks": [
{
"id": 1,
"name": "first task",
"category": {
"id": 1,
"name": "first category",
"tasks": [...]
}
},
{
"id": 2,
"name": "second task",
"category": {
"id": 1,
"name": "first category",
"tasks": [...]
}
}
]
}
}
]
}
I have a entity A with a manyToOne relation to another entity B.
I have implemented the reverse-side, to be able to retrieve the related A entities on the B one.
class Task
{
/**
* @ORM\ManyToOne(targetEntity="List", inversedBy="task")
* @ORM\JoinColumn(name="list_id", referencedColumnName="id")
*/
private $list;
public function toArray($recursive = false)
{
$entityAsArray = get_object_vars($this);
if ($recursive) {
foreach ($entityAsArray as &$var) {
if ((is_object($var)) && (method_exists($var, 'toArray'))) {
$var = $var->toArray($recursive);
}
}
}
return $entityAsArray;
}
}
use Doctrine\Common\Collections\ArrayCollection;
class List
{
/**
* @ORM\OneToMany(targetEntity="Task", mappedBy="list")
*/
private $tasks;
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}
Then I am building the different API routes and controllers,
Rendering the output as JsonResponses,
And I would like to render, for a given list, the different tasks using the route :
/api/v1/lists/1/tasks
The task action of my controller :
public function tasksAction($id)
{
$em = $this->getDoctrine()->getManager();
$list = $em->getRepository('MyRestBundle:List')->findOneActive($id);
if (!$list) {
throw $this->createNotFoundException('List undefined');
}
$tasks = $list->getTasks()->toArray();
foreach ($tasks as &$task) {
// recursively format the tasks as array
$task = $task->toArray(true);
}
$serializer = $this->get('serializer');
return $this->generateJsonResponse($serializer->normalize($tasks), 200);
}
But unfortunately, I always get a memory leak, because the call of toArray() is recursive, so each task has a list property which has a tasks collection etc.
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 130968 bytes) in src/Symfony/Component/Serializer/Serializer.php on line 146
I am wondering what would be the cleanest way to render entities with relations as JSON objects with Symfony2 ?
Do I really have to loop on my tasks to execute the "toArray()" method ?
I have also tried without it, without more success, except that the leak in in the file : src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php...
I have also tried without the JMSSeralizer, and the memory leak is thrown in my own php file.
Of course, I could increase the memory limit, but as it is an infinite loop problem of toArray() calls, it will not solve my problem.
How to format it properly ?