如何在 Symfony 2.0 AJAX 应用程序中将 Doctrine 实体编码为 JSON?

发布于 2024-11-24 23:30:14 字数 1128 浏览 0 评论 0原文

我正在开发游戏应用程序并使用 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。我知道我可以使用规范化器并对所有实体进行编码。但是,如果一个实体具有到其他实体的循环链接怎么办?或者实体图很大?您有什么建议吗?

我考虑一些实体的编码模式...或使用 NormalizedInterface 来避免循环...,

I'm developing game app and using Symfony 2.0. I have many AJAX requests to the backend. And more responses is converting entity to JSON. For example:

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

And all my controllers do the same thing: get an entity and encode some of its fields to JSON. I know that I can use normalizers and encode all entitities. But what if an entity has cycled links to other entity? Or the entities graph is very big? Do you have any suggestions?

I think about some encoding schema for entities... or using NormalizableInterface to avoid cycling..,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

风轻花落早 2024-12-01 23:30:14

现在使用 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);

With php5.4 now you can do :

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

And then call

json_encode(MyUserEntity);
假扮的天使 2024-12-01 23:30:14

另一种选择是使用 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;

Another option is to use the JMSSerializerBundle. In your controller you then do

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

You can configure how the serialization is done by using annotations in the entity class. See the documentation in the link above. For example, here's how you would exclude linked entities:

 /**
* 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;
童话 2024-12-01 23:30:14

您可以使用以下方法自动编码为 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');

You can automatically encode into Json, your complex entity with:

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');
杀お生予夺 2024-12-01 23:30:14

为了完成答案:Symfony2 附带了一个 json_encode 的包装器:
Symfony/Component/HttpFoundation/JsonResponse

控制器中的典型用法:

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

To complete the answer: Symfony2 comes with a wrapper around json_encode:
Symfony/Component/HttpFoundation/JsonResponse

Typical usage in your Controllers:

...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}
一影成城 2024-12-01 23:30:14

我发现序列化实体问题的解决方案如下:

#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

I found the solution to the problem of serializing entities was as follows:

#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 }

in my controller:

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

other example:

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

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

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

return new Response($json);

you can even configure it to deserialize arrays in http://api.symfony.com/2.0

極樂鬼 2024-12-01 23:30:14

我只需要解决同样的问题:对一个实体(“用户”)进行 json 编码,该实体与另一个实体(“位置”)具有一对多双向关联。

我尝试了几件事,我想现在我找到了最好的可接受的解决方案。这个想法是使用与 David 编写的相同的代码,但以某种方式通过告诉规范化器在某个点停止来拦截无限递归。

我不想实现自定义规范化器,因为在我看来,这个 GetSetMethodNormalizer 是一个很好的方法(基于反射等)。因此,我决定对其进行子类化,乍一看这并不简单,因为表示是否包含属性的方法 (isGetMethod) 是私有的。

但是,人们可以重写规范化方法,因此我在这一点上进行了拦截,只需取消设置引用“位置”的属性即可,因此无限循环被中断。

在代码中它看起来像这样:

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

} 

I just had to solve the same problem: json-encoding an entity ("User") having a One-To-Many Bidirectional Association to another Entity ("Location").

I tried several things and I think now I found the best acceptable solution. The idea was to use the same code as written by David, but somehow intercept the infinite recursion by telling the Normalizer to stop at some point.

I did not want to implement a custom normalizer, as this GetSetMethodNormalizer is a nice approach in my opinion (based on reflection etc.). So I've decided to subclass it, which is not trivial at first sight, because the method to say if to include a property (isGetMethod) is private.

But, one could override the normalize method, so I intercepted at this point, by simply unsetting the property that references "Location" - so the inifinite loop is interrupted.

In code it looks like this:

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

} 
不念旧人 2024-12-01 23:30:14

我遇到了同样的问题,我选择创建自己的编码器,它将通过递归来自行应对。

我创建了实现 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

I had the same problem and I chosed to create my own encoder, which will cope by themself with recursion.

I created classes which implements Symfony\Component\Serializer\Normalizer\NormalizerInterface, and a service which holds every 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);
}

An example of a Normalizer :

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

}

In a controller :

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

The complete code is here : https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer

再见回来 2024-12-01 23:30:14

在 Symfony 2.3

/app/config/config.yml

framework:
    # сервис конвертирования объектов в массивы, json, xml и обратно
    serializer:
        enabled: true

services:
    object_normalizer:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
        tags:
        # помечаем к чему относится этот сервис, это оч. важно, т.к. иначе работать не будет
          - { name: serializer.normalizer }

和控制器示例中:

/**
 * Поиск сущности по ИД объекта и ИД языка
 * @Route("/search/", name="orgunitSearch")
 */
public function orgunitSearchAction()
{
    $array = $this->get('request')->query->all();

    $entity = $this->getDoctrine()
        ->getRepository('IntranetOrgunitBundle:Orgunit')
        ->findOneBy($array);

    $serializer = $this->get('serializer');
    //$json = $serializer->serialize($entity, 'json');
    $array = $serializer->normalize($entity);

    return new JsonResponse( $array );
}

但是字段类型 \DateTime 的问题仍然存在。

in Symfony 2.3

/app/config/config.yml

framework:
    # сервис конвертирования объектов в массивы, json, xml и обратно
    serializer:
        enabled: true

services:
    object_normalizer:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
        tags:
        # помечаем к чему относится этот сервис, это оч. важно, т.к. иначе работать не будет
          - { name: serializer.normalizer }

and example for your controller:

/**
 * Поиск сущности по ИД объекта и ИД языка
 * @Route("/search/", name="orgunitSearch")
 */
public function orgunitSearchAction()
{
    $array = $this->get('request')->query->all();

    $entity = $this->getDoctrine()
        ->getRepository('IntranetOrgunitBundle:Orgunit')
        ->findOneBy($array);

    $serializer = $this->get('serializer');
    //$json = $serializer->serialize($entity, 'json');
    $array = $serializer->normalize($entity);

    return new JsonResponse( $array );
}

but the problems with the field type \DateTime will remain.

甜宝宝 2024-12-01 23:30:14

这更多的是一个更新(对于 Symfony v:2.7+ 和 JmsSerializer v:0.13.*@dev),以避免 Jms 尝试加载和序列化整个对象图(或者在循环的情况下)关系..)

模型:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;  
use JMS\Serializer\Annotation\Exclude;  
use JMS\Serializer\Annotation\MaxDepth; /* <=== Required */
/**
 * User
 *
 * @ORM\Table(name="user_table")
///////////////// OTHER Doctrine proprieties //////////////
 */
 public class User
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected   $id;

    /**
     * @ORM\ManyToOne(targetEntity="FooBundle\Entity\Game")
     * @ORM\JoinColumn(nullable=false)
     * @MaxDepth(1)
     */
    protected $game;
   /*
      Other proprieties ....and Getters ans setters
      ......................
      ......................
   */

在动作内部:

use JMS\Serializer\SerializationContext;
  /* Necessary include to enbale max depth */

  $users = $this
              ->getDoctrine()
              ->getManager()
              ->getRepository("FooBundle:User")
              ->findAll();

  $serializer = $this->container->get('jms_serializer');
  $jsonContent = $serializer
                   ->serialize(
                        $users, 
                        'json', 
                        SerializationContext::create()
                                 ->enableMaxDepthChecks()
                  );

  return new Response($jsonContent);

This is more an update (for Symfony v:2.7+ and JmsSerializer v:0.13.*@dev), so to avoid that Jms tries to load and serialise the whole object graph ( or in case of cyclic relation ..)

Model:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;  
use JMS\Serializer\Annotation\Exclude;  
use JMS\Serializer\Annotation\MaxDepth; /* <=== Required */
/**
 * User
 *
 * @ORM\Table(name="user_table")
///////////////// OTHER Doctrine proprieties //////////////
 */
 public class User
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected   $id;

    /**
     * @ORM\ManyToOne(targetEntity="FooBundle\Entity\Game")
     * @ORM\JoinColumn(nullable=false)
     * @MaxDepth(1)
     */
    protected $game;
   /*
      Other proprieties ....and Getters ans setters
      ......................
      ......................
   */

Inside an Action:

use JMS\Serializer\SerializationContext;
  /* Necessary include to enbale max depth */

  $users = $this
              ->getDoctrine()
              ->getManager()
              ->getRepository("FooBundle:User")
              ->findAll();

  $serializer = $this->container->get('jms_serializer');
  $jsonContent = $serializer
                   ->serialize(
                        $users, 
                        'json', 
                        SerializationContext::create()
                                 ->enableMaxDepthChecks()
                  );

  return new Response($jsonContent);
平安喜乐 2024-12-01 23:30:14

如果您使用 Symfony 2.7 或更高版本,并且不想包含任何额外的序列化包,也许您可​​以按照这种方式将学说实体序列化为 json -

  1. 在我的(常见的,父)控制器,我有一个准备序列化器的函数

    使用 Symfony\Component\Serializer\Encoder\JsonEncoder;
    使用 Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    使用 Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
    使用 Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    使用 Symfony\Component\Serializer\Serializer;
    
    // -----------------------------
    
    /**
     * @return 序列化器
     */
    受保护函数_getSerializer()
    {  
        $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
        $normalizer = new ObjectNormalizer($classMetadataFactory);
    
        返回 new Serializer([$normalizer], [new JsonEncoder()]);
    }
    
  2. 然后用它来将实体序列化为 JSON

    $this->_getSerializer()->normalize($anEntity, 'json');
    $this->_getSerializer()->normalize($arrayOfEntities, 'json');
    

完成!

但您可能需要一些微调。例如 -

If you are using Symfony 2.7 or above, and don't want to include any additional bundle for serializing, maybe you can follow this way to seialize doctrine entities to json -

  1. In my (common, parent) controller, I have a function that prepares the serializer

    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\Serializer\Serializer;
    
    // -----------------------------
    
    /**
     * @return Serializer
     */
    protected function _getSerializer()
    {  
        $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
        $normalizer           = new ObjectNormalizer($classMetadataFactory);
    
        return new Serializer([$normalizer], [new JsonEncoder()]);
    }
    
  2. Then use it to serialize Entities to JSON

    $this->_getSerializer()->normalize($anEntity, 'json');
    $this->_getSerializer()->normalize($arrayOfEntities, 'json');
    

Done!

But you may need some fine tuning. For example -

且行且努力 2024-12-01 23:30:14

当您需要在 Symfony 上创建大量 REST API 端点时,
最好的方法是使用以下捆绑包堆栈:

  1. JMSSerializerBundle 用于 Doctrine 实体的序列化
  2. FOSRestBundle 用于响应视图侦听器的捆绑包。此外,它还可以根据控制器/操作名称生成路由定义。
  3. NelmioApiDocBundle 自动生成在线文档和沙箱(允许在没有任何外部工具的情况下测试端点)。

正确配置所有内容后,实体代码将如下所示:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Table(name="company")
 */
class Company
{

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     *
     * @JMS\Expose()
     * @JMS\SerializedName("name")
     * @JMS\Groups({"company_overview"})
     */
    private $name;

    /**
     * @var Campaign[]
     *
     * @ORM\OneToMany(targetEntity="Campaign", mappedBy="company")
     * 
     * @JMS\Expose()
     * @JMS\SerializedName("campaigns")
     * @JMS\Groups({"campaign_overview"})
     */
    private $campaigns;
}

然后,控制器中的代码:

use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\View;

class CompanyController extends Controller
{

    /**
     * Retrieve all companies
     *
     * @View(serializerGroups={"company_overview"})
     * @ApiDoc()
     *
     * @return Company[]
     */
    public function cgetAction()
    {
        return $this->getDoctrine()->getRepository(Company::class)->findAll();
    }
}

这种设置的好处是:

  • 可以将实体中的 @JMS\Expose() 注释添加到简单的字段,以及任何类型的关系。此外,还可以公开某些方法执行的结果(使用注释@JMS\VirtualProperty())
  • 通过序列化组,我们可以在不同情况下控制公开的字段。
  • 控制器非常简单。 action方法可以直接返回一个实体或实体数组,并且它们会自动序列化。
  • @ApiDoc() 允许直接从浏览器测试端点,无需任何 REST 客户端或 JavaScript 代码

When you need to create a lot of REST API endpoints on Symfony,
the best way is to use the following stack of bundles:

  1. JMSSerializerBundle for the serialization of Doctrine entities
  2. FOSRestBundle bundle for response view listener. Also, it can generate definitions of routes based on controller/action name.
  3. NelmioApiDocBundle to auto-generate online documentation and Sandbox(which allows testing endpoint without any external tool).

When you configure everything properly, you entity code will look like this:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Table(name="company")
 */
class Company
{

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     *
     * @JMS\Expose()
     * @JMS\SerializedName("name")
     * @JMS\Groups({"company_overview"})
     */
    private $name;

    /**
     * @var Campaign[]
     *
     * @ORM\OneToMany(targetEntity="Campaign", mappedBy="company")
     * 
     * @JMS\Expose()
     * @JMS\SerializedName("campaigns")
     * @JMS\Groups({"campaign_overview"})
     */
    private $campaigns;
}

Then, code in controller:

use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\View;

class CompanyController extends Controller
{

    /**
     * Retrieve all companies
     *
     * @View(serializerGroups={"company_overview"})
     * @ApiDoc()
     *
     * @return Company[]
     */
    public function cgetAction()
    {
        return $this->getDoctrine()->getRepository(Company::class)->findAll();
    }
}

The benefits of such a set up are:

  • @JMS\Expose() annotations in the entity can be added to simple fields, and to any type of relations. Also, there is the possibility to expose the result of some method execution (use annotation @JMS\VirtualProperty() for that)
  • With serialization groups, we can control exposed fields in different situations.
  • Controllers are very simple. The action method can directly return an entity or array of entities, and they will be automatically serialized.
  • And @ApiDoc() allows testing the endpoint directly from the browser, without any REST client or JavaScript code
画骨成沙 2024-12-01 23:30:14

现在,您还可以使用 Doctrine ORM 转换 将实体转换为嵌套标量数组并返回

Now you can also use Doctrine ORM Transformations to convert entities to nested arrays of scalars and back

静若繁花 2024-12-01 23:30:14

接受的答案是正确的,但如果您需要序列化 ​​Entity 的过滤子集, json_encode 就足够了:

考虑这个例子:

class FileTypeRepository extends ServiceEntityRepository  
{

   const ALIAS = 'ft';
   const SHORT_LIST = 'ft.name name';

    public function __construct(ManagerRegistry $registry)
    {
         parent::__construct($registry, FileType::class);
    }

    public function getAllJsonFileTypes() 
    {
        return json_encode($this->getAllFileTypes());
    } 

   /**
    * @return array
    */
    public function getAllFileTypes()
    {
        $query = $this->createQueryBuilder(self::ALIAS);
        $query->select(self::SHORT_LIST);
        return $query->getQuery()->getResult();
    }
}



/** THIS IS ENOUGH TO SERIALIZE AN ARRAY OF ENTITIES SINCE the doctrine SELECT will remove complex data structures from the entities itself **/
json_encode($this->getAllFileTypes()); 

简短说明:至少在 Symfony 5.1 上测试

The accepted answer is correct but if You'll need to serialize a filtered subset of an Entity , json_encode is enough:

Consider this example:

class FileTypeRepository extends ServiceEntityRepository  
{

   const ALIAS = 'ft';
   const SHORT_LIST = 'ft.name name';

    public function __construct(ManagerRegistry $registry)
    {
         parent::__construct($registry, FileType::class);
    }

    public function getAllJsonFileTypes() 
    {
        return json_encode($this->getAllFileTypes());
    } 

   /**
    * @return array
    */
    public function getAllFileTypes()
    {
        $query = $this->createQueryBuilder(self::ALIAS);
        $query->select(self::SHORT_LIST);
        return $query->getQuery()->getResult();
    }
}



/** THIS IS ENOUGH TO SERIALIZE AN ARRAY OF ENTITIES SINCE the doctrine SELECT will remove complex data structures from the entities itself **/
json_encode($this->getAllFileTypes()); 

Short note: Tested at least on Symfony 5.1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文