[ajax] Symfony 2.0 AJAX 애플리케이션에서 Doctrine 엔티티를 JSON으로 인코딩하는 방법은 무엇입니까?

게임 앱을 개발 중이며 Symfony 2.0을 사용하고 있습니다. 백엔드에 많은 AJAX 요청이 있습니다. 그리고 더 많은 응답이 엔티티를 JSON으로 변환하고 있습니다. 예를 들면 :

class DefaultController extends Controller
{
    public function launchAction()
    {
        $user = $this->getDoctrine()
                     ->getRepository('UserBundle:User')
                     ->find($id);

        // encode user to json format
        $userDataAsJson = $this->encodeUserDataToJson($user);
        return array(
            'userDataAsJson' => $userDataAsJson
        );
    }

    private function encodeUserDataToJson(User $user)
    {
        $userData = array(
            'id' => $user->getId(),
            'profile' => array(
                'nickname' => $user->getProfile()->getNickname()
            )
        );

        $jsonEncoder = new JsonEncoder();
        return $jsonEncoder->encode($userData, $format = 'json');
    }
}

그리고 모든 컨트롤러는 동일한 작업을 수행합니다. 엔터티를 가져 와서 일부 필드를 JSON으로 인코딩합니다. 노멀 라이저를 사용하고 모든 엔터티를 인코딩 할 수 있다는 것을 알고 있습니다. 그러나 한 엔티티가 다른 엔티티에 대한 링크를 순환했다면 어떻게 될까요? 아니면 엔티티 그래프가 매우 큽니까? 의견 있으십니까?

엔터티에 대한 인코딩 스키마에 대해 생각합니다 … 또는 NormalizableInterface순환을 피하기 위해 사용 합니다 ..,



답변

또 다른 옵션은 JMSSerializerBundle 을 사용하는 입니다. 컨트롤러에서 다음을 수행하십시오.

$serializer = $this->container->get('serializer');
$reports = $serializer->serialize($doctrineobject, 'json');
return new Response($reports); // should be $reports as $doctrineobject is not serialized

엔티티 클래스의 주석을 사용하여 직렬화가 수행되는 방법을 구성 할 수 있습니다. 위 링크의 문서를 참조하십시오. 예를 들어, 연결된 항목을 제외하는 방법은 다음과 같습니다.

 /**
* Iddp\RorBundle\Entity\Report
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Iddp\RorBundle\Entity\ReportRepository")
* @ExclusionPolicy("None")
*/
....
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="reports")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
* @Exclude
*/
protected $client;


답변

이제 php5.4로 다음을 수행 할 수 있습니다.

use JsonSerializable;

/**
* @Entity(repositoryClass="App\Entity\User")
* @Table(name="user")
*/
class MyUserEntity implements JsonSerializable
{
    /** @Column(length=50) */
    private $name;

    /** @Column(length=50) */
    private $login;

    public function jsonSerialize()
    {
        return array(
            'name' => $this->name,
            'login'=> $this->login,
        );
    }
}

그리고 전화

json_encode(MyUserEntity);


답변

다음을 사용하여 복잡한 엔터티 인 Json으로 자동으로 인코딩 할 수 있습니다.

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;

$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');


답변

답을 완성하기 위해 : Symfony2는 json_encode를 둘러싼 래퍼와 함께 제공됩니다 :
Symfony / Component / HttpFoundation / JsonResponse

컨트롤러의 일반적인 사용 :

...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}

도움이 되었기를 바랍니다

제이


답변

엔티티 직렬화 문제에 대한 해결책은 다음과 같습니다.

#config/config.yml

services:
    serializer.method:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
    serializer.encoder.json:
        class: Symfony\Component\Serializer\Encoder\JsonEncoder
    serializer:
        class: Symfony\Component\Serializer\Serializer
        arguments:
            - [@serializer.method]
            - {json: @serializer.encoder.json }

내 컨트롤러에서 :

$serializer = $this->get('serializer');

$entity = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findOneBy($params);


$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$toEncode = array(
    'response' => array(
        'entity' => $serializer->normalize($entity),
        'entities' => $serializer->normalize($collection)
    ),
);

return new Response(json_encode($toEncode));

다른 예 :

$serializer = $this->get('serializer');

$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$json = $serializer->serialize($collection, 'json');

return new Response($json);

http://api.symfony.com/2.0 에서 배열을 직렬화 해제하도록 구성 할 수도 있습니다.


답변

동일한 문제를 해결해야했습니다. 하나의 엔티티 ( “사용자”)가 다른 엔티티 ( “위치”)와 일대 다 양방향 연관성을 갖는 json 인코딩입니다.

여러 가지를 시도해 보았는데 이제 가장 적합한 솔루션을 찾은 것 같습니다. 아이디어는 David가 작성한 것과 동일한 코드를 사용하는 것이었지만 노멀 라이저에게 특정 지점에서 중지하도록 지시하여 어떻게 든 무한 재귀를 가로 챌 수 있습니다.

이 GetSetMethodNormalizer는 제 생각에 (반사 등에 기반한) 좋은 접근 방식이므로 사용자 지정 노멀 라이저를 구현하고 싶지 않았습니다. 그래서 속성 (isGetMethod)을 포함할지 여부를 말하는 메서드가 비공개이기 때문에 처음에는 사소하지 않은 하위 클래스로 결정했습니다.

그러나 정규화 메서드를 재정의 할 수 있으므로이 시점에서 “Location”을 참조하는 속성을 설정 해제하여 가로 채었습니다. 따라서 inifinite 루프가 중단됩니다.

코드에서는 다음과 같습니다.

class GetSetMethodNormalizer extends \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer {

    public function normalize($object, $format = null)
    {
        // if the object is a User, unset location for normalization, without touching the original object
        if($object instanceof \Leonex\MoveBundle\Entity\User) {
            $object = clone $object;
            $object->setLocations(new \Doctrine\Common\Collections\ArrayCollection());
        }

        return parent::normalize($object, $format);
    }

}


답변

나는 같은 문제가 있었고 재귀에 스스로 대처할 자체 인코더를 만들기로 결정했습니다.

구현하는 클래스 Symfony\Component\Serializer\Normalizer\NormalizerInterface와 모든 NormalizerInterface.

#This is the NormalizerService

class NormalizerService
{

   //normalizer are stored in private properties
   private $entityOneNormalizer;
   private $entityTwoNormalizer;

   public function getEntityOneNormalizer()
   {
    //Normalizer are created only if needed
    if ($this->entityOneNormalizer == null)
        $this->entityOneNormalizer = new EntityOneNormalizer($this); //every normalizer keep a reference to this service

    return $this->entityOneNormalizer;
   }

   //create a function for each normalizer



  //the serializer service will also serialize the entities 
  //(i found it easier, but you don't really need it)
   public function serialize($objects, $format)
   {
     $serializer = new Serializer(
            array(
                $this->getEntityOneNormalizer(),
                $this->getEntityTwoNormalizer()
            ),
            array($format => $encoder) );

     return $serializer->serialize($response, $format);
}

노멀 라이저의 예 :

use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class PlaceNormalizer implements NormalizerInterface {

private $normalizerService;

public function __construct($normalizerService)
{
    $this->service = normalizerService;

}

public function normalize($object, $format = null) {
    $entityTwo = $object->getEntityTwo();
    $entityTwoNormalizer = $this->service->getEntityTwoNormalizer();

    return array(
        'param' => object->getParam(),
        //repeat for every parameter
        //!!!! this is where the entityOneNormalizer dealt with recursivity
        'entityTwo' => $entityTwoNormalizer->normalize($entityTwo, $format.'_without_any_entity_one') //the 'format' parameter is adapted for ignoring entity one - this may be done with different ways (a specific method, etc.)
    );
}

}

컨트롤러에서 :

$normalizerService = $this->get('normalizer.service'); //you will have to configure services.yml
$json = $normalizerService->serialize($myobject, 'json');
return new Response($json);

완전한 코드는 여기에 있습니다 : https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer