将包含递归关系的实体呈现为JSON
我正在使用带Doctrine2的Symfony2.4构建一个基于JSON的RESTAPI 编辑:使用JsonNormalizer,我可以禁用一些属性,但是如果我想设置它们,而不需要递归性呢 基本上,我现在(工作)的是: 我想要的是:将包含递归关系的实体呈现为JSON,json,rest,symfony,doctrine-orm,entity-relationship,Json,Rest,Symfony,Doctrine Orm,Entity Relationship,我正在使用带Doctrine2的Symfony2.4构建一个基于JSON的RESTAPI 编辑:使用JsonNormalizer,我可以禁用一些属性,但是如果我想设置它们,而不需要递归性呢 基本上,我现在(工作)的是: 我想要的是: { "tasks": [ { "id": 1, "name": "first task", "category": { "id": 1,
{
"tasks": [
{
"id": 1,
"name": "first task",
"category": {
"id": 1,
"name": "first category"
}
},
{
"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": 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": [...]
}
}
]
}
}
]
}
我有一个实体a,它与另一个实体B有多个关系
我实现了相反的一面,以便能够检索B one上的相关A实体。
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();
}
}
然后我构建不同的API路由和控制器,
将输出呈现为JsonResponses,
对于给定的列表,我想使用路径呈现不同的任务:
/api/v1/lists/1/tasks
我的控制器的任务操作:
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);
}
但不幸的是,我总是遇到内存泄漏,因为toArray()的调用是递归的,所以每个任务都有一个list属性,其中包含一个tasks集合等
PHP致命错误:第146行src/Symfony/Component/Serializer/Serializer.PHP中允许的内存大小为134217728字节(尝试分配130968字节)
我想知道用Symfony2将具有关系的实体呈现为JSON对象的最干净的方法是什么
我真的必须在任务上循环才能执行“toArray()”方法吗
我也尝试过不使用它,但没有更多的成功,除了文件中的漏洞:src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
我也尝试过不使用JMSSeralizer,内存泄漏被抛出到我自己的php文件中
当然,我可以增加内存限制,但由于这是toArray()调用的无限循环问题,因此它无法解决我的问题
如何正确格式化?编辑
如果我理解您的代码在做什么,我认为toArray($recursive)函数会直接进入无限递归(只要$var=$this),也会间接进入无限递归(即,通过同级循环遍历自己的列表并再次调用原始任务的toArray)。尝试跟踪已处理的内容以防止无限递归:
/**
* @ORM\ManyToOne(targetEntity="List", inversedBy="task")
* @ORM\JoinColumn(name="list_id", referencedColumnName="id")
*/
private $list;
private $toArrayProcessed = array();
public function toArray($recursive = false)
{
$this->toArrayProcessed[$this->getId()] = 1;
$entityAsArray = get_object_vars($this);
if ($recursive) {
foreach ($entityAsArray as &$var) {
if ((is_object($var)) && (method_exists($var, 'toArray')) && !isset($this->toArrayProcessed[$var->getId()]) {
$var = $var->toArray($recursive);
}
}
}
return $entityAsArray;
}
编辑
如果我理解您的代码在做什么,我认为toArray($recursive)函数会直接进入无限递归(只要$var=$this),也会间接进入无限递归(即,通过同级循环遍历自己的列表并再次调用原始任务的toArray)。尝试跟踪已处理的内容以防止无限递归:
/**
* @ORM\ManyToOne(targetEntity="List", inversedBy="task")
* @ORM\JoinColumn(name="list_id", referencedColumnName="id")
*/
private $list;
private $toArrayProcessed = array();
public function toArray($recursive = false)
{
$this->toArrayProcessed[$this->getId()] = 1;
$entityAsArray = get_object_vars($this);
if ($recursive) {
foreach ($entityAsArray as &$var) {
if ((is_object($var)) && (method_exists($var, 'toArray')) && !isset($this->toArrayProcessed[$var->getId()]) {
$var = $var->toArray($recursive);
}
}
}
return $entityAsArray;
}
我有一种感觉,我们可能想得太多了。那对你有用吗
// In your controller
$repo = $this->getDoctrine()->getRepository('MyRestBundle:List');
$list = $repo->findActiveOne($id);
$tasks = $list->getTasks()->toArray();
$serializer = $this->get('serializer');
$json = $serializer->serialize($tasks, 'json');
为什么任务实体是递归的?一个任务不能包含另一个任务。只有列表才能包含任务数组。所以基本上我们要做的就是从列表实体中获取这个数组并序列化它。除非我错过了什么
编辑:
您可以要求序列化程序忽略以下内容中提到的某些属性:
尝试按照该示例,忽略任务
实体中的$list
属性
EDIT2:
您不需要在每个任务中都包含“列表”,因为它是相同的。您的json应该具有相同级别的“列表”和“任务”。然后,“任务”将是一个不包含“列表”的任务数组。要实现这一点,您可以使用类似于
数组('list'=>$list,'tasks'=>$tasks)
的东西,并将其序列化。我觉得我们可能想得太多了。那对你有用吗
// In your controller
$repo = $this->getDoctrine()->getRepository('MyRestBundle:List');
$list = $repo->findActiveOne($id);
$tasks = $list->getTasks()->toArray();
$serializer = $this->get('serializer');
$json = $serializer->serialize($tasks, 'json');
为什么任务实体是递归的?一个任务不能包含另一个任务。只有列表才能包含任务数组。所以基本上我们要做的就是从列表实体中获取这个数组并序列化它。除非我错过了什么
编辑:
您可以要求序列化程序忽略以下内容中提到的某些属性:
尝试按照该示例,忽略任务
实体中的$list
属性
EDIT2:
您不需要在每个任务中都包含“列表”,因为它是相同的。您的json应该具有相同级别的“列表”和“任务”。然后,“任务”将是一个不包含“列表”的任务数组。要实现这一点,您可以使用类似于
array('list'=>$list,'tasks'=>$tasks)
的东西,并将其序列化。这正是我的问题。不幸的是,此解决方案不起作用,因为属性$tasks的类型为“Doctrine\ORM\PersistentCollection”,它实现了“toArray()”方法,但没有ID。它可以与直接实体一起工作,但不能与实体数组一起工作!这正是我的问题。不幸的是,此解决方案不起作用,因为属性$tasks的类型为“Doctrine\ORM\PersistentCollection”,它实现了“toArray()”方法,但没有ID。它可以与直接实体一起工作,但不能与实体数组一起工作!我可以在实体列表中添加这样的getter,但为什么呢?我应该在任何地方叫它吗?我改变了上面的例子。也许这会更有用任务实体不是递归的。但我实现了列表和任务的反面。因此,任务是列表的一部分,列表可以包含一些任务。问题是,由于这个反面,关系是无限的。。。一个任务一个有任务的列表一个有任务列表的列表等等。我重试了你的方法,但它仍然不起作用,对不起。哦,真的谢谢,这是我完全错过的文档中的一点!因此,现在,当我移除与setIgnoredAttributes()的关系时,它至少工作得很好。但是如果我想要呢?如果你想要什么呢?你能详细说明一下吗?我可以在我的实体列表中添加这样一种getter,但为什么呢?我应该在任何地方叫它吗?我改变了上面的例子。也许这会更有用任务实体不是递归的。但我实现了列表和任务的反面。因此,任务是列表的一部分,列表可以包含一些任务。问题是这些关系是相互关联的