我正在使用带有Doctrine2的Symfony2.4构建基于JSON的REST API。
编辑:使用JsonNormalizer,我可以禁用一些属性,但是如果我想设置它们而没有递归呢?基本上,我现在拥有的(工作)是:
{
"tasks": [
{
"id": 1,
"name": "first task"
},
{
"id": 2,
"name": "second task"
}
]
}
我想要的是:
{
"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": [...]
}
}
]
}
}
]
}
我有一个与另一个实体B有很多关系的实体A.
我已经实现了反面,以便能够检索B上的相关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属性,它有一个任务集合等。
PHP致命错误:第146行的src / Symfony / Component / Serializer / Serializer.php中允许的内存大小为134217728字节(试图分配130968字节)
我想知道将具有关系的实体作为JSON对象与Symfony2一起呈现的最简洁方法是什么?
我是否真的必须循环执行我的任务来执行" toArray()"方法?
我也试过没有它,没有更多的成功,除了文件中的泄漏:src / Symfony / Component / Serializer / Normalizer / GetSetMethodNormalizer.php ...
我也尝试过没有JMSSeralizer,并且在我自己的php文件中抛出了内存泄漏。
当然,我可以增加内存限制,但因为它是toArray()调用的无限循环问题,所以它不会解决我的问题。
如何正确格式化?
答案 0 :(得分:3)
我有一种感觉,我们可能会过度思考这一点。这对你有用吗?
// 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');
为什么Task Entity是递归的?任务不能包含其他任务。只有List才能包含一系列任务。所以基本上我们应该做的就是从List实体获取这个数组并将其序列化。除非我遗漏了什么。
修改强>
您可以要求序列化程序忽略documentation:
中提到的某些属性use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
$normalizer = new GetSetMethodNormalizer();
$normalizer->setIgnoredAttributes(array('age'));
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
$serializer->serialize($person, 'json'); // Output: {"name":"foo"}
尝试按照该示例操作,只需忽略$list
实体中的Task
属性。
<强> EDIT2:强>
你不需要在每个任务中使用'list',因为它是相同的。你的json应该在同一级别有'list'和'tasks'。然后'任务'将是一系列不包含'list'的任务。要实现这一目标,您可以使用array('list' => $list, 'tasks' => $tasks)
之类的内容并序列化。
答案 1 :(得分:1)
修改强>
如果我理解你的代码在做什么,我认为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;
}