2017-06-23 10 views
0

を検出された私は、残りのAPIを構築するためにsymfonyのプロジェクトに取り組んでいる、私はそのような相互に関連する4つのエンティティがあります。のSymfony API:循環参照が

class diagram

を私はしました私は、例えば、リソースを取得したいとき、ちょうどGET Webサービスを構築するためにFOSRestBundleをインストール:

http://evaluation.dev/app_dev.php/api/families

私はこのエラーを得た:

メッセージ: "循環参照が検出された(構成 限界:1)。"、

これは私のコントローラである:

<?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; 
    } 
} 

任意のsolutioどう?

+0

循環参照が一般的です。参照してください:http://symfony.com/doc/current/components/serializer.html#handling-circular-references、私はJMSSerializerBundleを使用する必要があります... –

答えて

2

はあなたたとえば、あなたはこの

$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')); 

ようCircularReferenceを回避することができますしかし、あなたの例では、あなたのコントローラにビューのFOSRestBundleを使用しないでください。 FOSRestControllerは、handleView()メソッドとview()メソッドを提供します。ためにあなたのアプリケーション/設定/ config.ymlで

framework: 
    serializer: { enabled: true } 

:この場合は、この

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); 
    } 

と同様に、シリアライザがサービスで、このサービスはconfig.ymlで活性化されますcircularReferenceを避けるために、これを行うことができます。

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"] 

あなたのアプリケーション/設定/ services.ymlでは、工場はこのようなことができます:

namespace AppBundle\Serializer; 

class CircularHandlerFactory 
{ 
    /** 
    * @return \Closure 
    */ 
    public static function getId() 
    { 
     return function ($object) { 
      return $object->getId(); 
     }; 
    } 
} 

もう一つのアイデアは、シリアライザのためのグループを使用することです。FosRestBundleによって指定された注釈があります: @Rest \ビュー(serializerGroupsは= { "ユーザー"})ここ

詳細情報:エンティティの関係を扱うとき https://symfony.com/doc/current/serializer.html#using-serialization-groups-annotations Symfony2, FOSRestBundle. How to use group with JMSSerializerBundle?