带有FOSRestBundle的Symfony API:已检测到循环引用
我正在进行一个symfony项目,以构建一个rest API,我有4个彼此相关的实体,如下所示: 我安装FOSRestBundle只是为了构建一个GET web服务,当我想要获取资源时,例如: 我得到了这个错误: 消息:“检测到循环引用(已配置 限制:1)。” 这是我的控制器:带有FOSRestBundle的Symfony API:已检测到循环引用,api,symfony,serialization,entity,fosrestbundle,Api,Symfony,Serialization,Entity,Fosrestbundle,我正在进行一个symfony项目,以构建一个rest API,我有4个彼此相关的实体,如下所示: 我安装FOSRestBundle只是为了构建一个GET web服务,当我想要获取资源时,例如: 我得到了这个错误: 消息:“检测到循环引用(已配置 限制:1)。” 这是我的控制器: <?php namespace API\APIBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use
<?php
namespace API\APIBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations as Rest;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Serializer;
class CartographyController extends Controller
{
/**
* @Rest\View()
* @Rest\Get("/posts")
* @param Request $request
* @return Response
*/
public function getPostsAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$posts = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Post')
->findAll();
return new Response($serializer->serialize($posts, 'json'));
}
/**
* @Rest\View()
* @Rest\Get("/employments")
* @param Request $request
* @return Response
*/
public function geEmploymentAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$employments = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Employment')
->findAll();
return new Response($serializer->serialize($employments, 'json'));
}
/**
* @Rest\View()
* @Rest\Get("/professions")
* @param Request $request
* @return Response
*/
public function geProfessionsAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$professions = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Profession')
->findAll();
return new Response($serializer->serialize($professions, 'json')); }
/**
* @Rest\View()
* @Rest\Get("/families")
* @param Request $request
* @return Response
*/
public function geFamiliesAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$families = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Family')
->findAll();
return new Response($serializer->serialize($families, 'json'));
}
}
<?php
namespace EvalBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* Family
*
* @ORM\Table(name="family")
* @ORM\Entity(repositoryClass="EvalBundle\Repository\FamilyRepository")
*/
class Family
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* One Family has Many Professions.
* @ORM\OneToMany(targetEntity="Profession", mappedBy="family",orphanRemoval=true,cascade={"persist", "remove"},fetch="EAGER")
*/
protected $professions;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Family
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return mixed
*/
public function getProfessions()
{
return $this->professions;
}
/**
* @param mixed $professions
*/
public function setProfessions($professions)
{
$this->professions = $professions;
}
public function __toString() {
return $this->name;
}
}
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Profession
*
* @ORM\Table(name="profession")
* @ORM\Entity(repositoryClass="EvalBundle\Repository\ProfessionRepository")
*/
class Profession
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many professions have One Family.
* @ORM\ManyToOne(targetEntity="Family", inversedBy="professions",cascade={"persist", "remove"})
*/
public $family;
/**
* One Profession has Many Employment.
* @ORM\OneToMany(targetEntity="Employment", mappedBy="profession",cascade={"persist", "remove"}, orphanRemoval=true,fetch="EAGER")
*/
private $employments;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Profession
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return mixed
*/
public function getEmployments()
{
return $this->employments;
}
/**
* @param mixed $employments
*/
public function setEmployments($employments)
{
$this->employments = $employments;
}
/**
* @return mixed
*/
public function getFamily()
{
return $this->family;
}
/**
* @param mixed $family
*/
public function setFamily($family)
{
$this->family = $family;
}
public function __toString() {
return $this->name;
}
}
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Post
*
* @ORM\Table(name="post")
* @ORM\Entity(repositoryClass="EvalBundle\Repository\PostRepository")
*/
class Post
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many Posts have One Employment.
* @ORM\ManyToOne(targetEntity="Employment", inversedBy="posts", fetch="EAGER")
* @ORM\JoinColumn(name="employment_id", referencedColumnName="id",nullable=false)
*/
public $employment;
/**
* One Post has Many LevelRequired.
* @ORM\OneToMany(targetEntity="RequiredLevel", mappedBy="post")
*/
private $requiredLevels;
/**
* One Post has Many PostEvaluation.
* @ORM\OneToMany(targetEntity="PostEvaluation", mappedBy="post")
*/
private $postEvaluations;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Post
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return mixed
*/
public function getEmployment()
{
return $this->employment;
}
public function getProfession(){
return $this->employment->profession;
}
public function getFamily(){
return $this->employment->profession->family;
}
/**
* @param mixed $employment
*/
public function setEmployment($employment)
{
$this->employment = $employment;
}
public function __toString() {
return $this->name;
}
}
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Employment
*
* @ORM\Table(name="employment")
* @ORM\Entity(repositoryClass="EvalBundle\Repository\EmploymentRepository")
*/
class Employment
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many Employments have One Profession.
* @ORM\ManyToOne(targetEntity="Profession", inversedBy="employments",fetch="EAGER")
*/
public $profession;
/**
* One Employment has Many post.
* @ORM\OneToMany(targetEntity="Post", mappedBy="employment",cascade={"persist", "remove"}, orphanRemoval=true,cascade={"persist", "remove"})
*/
private $posts;
/**
* One Employment has One Grid.
* @ORM\OneToOne(targetEntity="Grid", mappedBy="employment")
*/
private $grid;
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Employment
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return mixed
*/
public function getProfession()
{
return $this->profession;
}
/**
* @param mixed $profession
*/
public function setProfession($profession)
{
$this->profession = $profession;
}
/**
* @return mixed
*/
public function getPosts()
{
return $this->posts;
}
/**
* @param mixed $posts
*/
public function setPosts($posts)
{
$this->posts = $posts;
}
/**
* @return mixed
*/
public function getGrid()
{
return $this->grid;
}
/**
* @param mixed $grid
*/
public function setGrid($grid)
{
$this->grid = $grid;
}
public function __toString() {
return $this->name;
}
}
例如,您可以避免这样的循环引用
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceLimit(1);
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array(new JsonEncoder()));
var_dump($serializer->serialize($org, 'json'));
但在您的示例中,您没有对控制器中的视图使用FOSRestBundle。FOSRestController为您提供一个handleView()和一个view()方法。像这样
class CartographyController extends FOSRestController
{
public function getPostsAction(Request $request)
{
$posts = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Post')
->findAll();
$view = $this->view($posts, 200);
return $this->handleView($view);
}
在这种情况下,serialiser是一个服务,该服务在config.yml中激活:
在你的app/config/config.yml中
framework:
serializer: { enabled: true }
为了避免循环引用,可以这样做。
在你的app/config/services.yml中
circular_reference_handler:
public: false
class: callback
factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
arguments: ["@serializer.mapping.class_metadata_factory", null, "@serializer.property_accessor"]
public: false
tags: [serializer.normalizer]
calls:
- method: setCircularReferenceLimit
arguments: [1]
- method: setCircularReferenceHandler
arguments: ["@circular_reference_handler"]
工厂可以是这样的:
namespace AppBundle\Serializer;
class CircularHandlerFactory
{
/**
* @return \Closure
*/
public static function getId()
{
return function ($object) {
return $object->getId();
};
}
}
另一个想法是为序列化程序使用组。FosRestBundle给出了一个注释:
@Rest\View(serializerGroups={“用户”})
更多信息请点击此处:
例如,您可以避免这样的循环引用
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceLimit(1);
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array(new JsonEncoder()));
var_dump($serializer->serialize($org, 'json'));
但在您的示例中,您没有对控制器中的视图使用FOSRestBundle。FOSRestController为您提供一个handleView()和一个view()方法。像这样
class CartographyController extends FOSRestController
{
public function getPostsAction(Request $request)
{
$posts = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Post')
->findAll();
$view = $this->view($posts, 200);
return $this->handleView($view);
}
在这种情况下,serialiser是一个服务,该服务在config.yml中激活:
在你的app/config/config.yml中
framework:
serializer: { enabled: true }
为了避免循环引用,可以这样做。
在你的app/config/services.yml中
circular_reference_handler:
public: false
class: callback
factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
arguments: ["@serializer.mapping.class_metadata_factory", null, "@serializer.property_accessor"]
public: false
tags: [serializer.normalizer]
calls:
- method: setCircularReferenceLimit
arguments: [1]
- method: setCircularReferenceHandler
arguments: ["@circular_reference_handler"]
工厂可以是这样的:
namespace AppBundle\Serializer;
class CircularHandlerFactory
{
/**
* @return \Closure
*/
public static function getId()
{
return function ($object) {
return $object->getId();
};
}
}
另一个想法是为序列化程序使用组。FosRestBundle给出了一个注释:
@Rest\View(serializerGroups={“用户”})
更多信息请点击此处:
处理实体关系时,循环引用很常见。请参阅:,我认为您必须使用JMSSerializerBundle…在处理实体关系时,循环引用很常见。请看:,我认为您必须使用JMSSerializerBundle…遗憾的是,他们没有关于此类内容的任何文档。这就像“看,我们新的功能齐全的开源工具太棒了,但我们不会告诉任何人如何使用它。”遗憾的是,他们没有类似的文档。这就像“看,我们新的全功能开源工具太棒了,但我们不会告诉任何人如何使用它。”