如何在Symfony 2.0 AJAX应用程序中将条令实体编码为JSON?
我正在开发游戏应用程序并使用Symfony 2.0。我对后端有很多AJAX请求。更多的响应是将实体转换为JSON。例如:如何在Symfony 2.0 AJAX应用程序中将条令实体编码为JSON?,ajax,doctrine,doctrine-orm,symfony,Ajax,Doctrine,Doctrine Orm,Symfony,我正在开发游戏应用程序并使用Symfony 2.0。我对后端有很多AJAX请求。更多的响应是将实体转换为JSON。例如: class DefaultController extends Controller { public function launchAction() { $user = $this->getDoctrine() ->getRepository('UserBund
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
避免循环..,您可以自动将复杂实体编码为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');
我只需要解决同样的问题:json编码一个实体(“用户”)与另一个实体(“位置”)有一对多的双向关联 我尝试了几件事,我想现在我找到了最好的可接受的解决方案。其思想是使用与David编写的代码相同的代码,但通过告诉规范化程序在某个点停止,以某种方式拦截无限递归 我不想实现自定义规范化器,因为在我看来,这个GetSetMethodNormalizer是一个不错的方法(基于反射等)。所以我决定对它进行子类化,这乍一看并不简单,因为表示是否包含属性的方法(isGetMethod)是私有的 但是,可以重写normalize方法,所以我在这里截取了它,只需取消设置引用“Location”的属性,就可以中断有限循环 在代码中,它如下所示:
类GetSetMethodNormalizer扩展\Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer{
公共函数规格化($object,$format=null)
{
//如果对象是用户,则在不接触原始对象的情况下取消设置规范化的位置
if($object instanceof\Leonex\MoveBundle\Entity\User){
$object=克隆$object;
$object->setLocations(new\Doctrine\Common\Collections\ArrayCollection());
}
返回父项::normalize($object,$format);
}
}
另一个选项是使用。然后在控制器中执行以下操作:
$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;
我发现序列化实体问题的解决方案如下:
#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));
/**
* Поиск сущности по ИД объекта и ИД языка
* @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 );
}
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();
}
}
其他例子:
$serializer = $this->get('serializer');
$collection = $this->get('doctrine')
->getRepository('myBundle:Entity')
->findBy($params);
$json = $serializer->serialize($collection, 'json');
return new Response($json);
您甚至可以使用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);
我也有同样的问题,我选择创建自己的编码器,它将自己处理递归 我创建了实现
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);
完整的代码如下:要完成答案:Symfony2附带了一个围绕json_encode的包装器: 控制器中的典型用法:
...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}
在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 }
和控制器的示例:
$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));
/**
* Поиск сущности по ИД объекта и ИД языка
* @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 );
}
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();
}
}
但是字段类型\DateTime的问题仍然存在。这更像是一个更新(对于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
......................
......................
*/
如果您使用的是Symfony 2.7或更高版本,并且不想包含任何用于序列化的附加捆绑包,那么您可以按照这种方式将条令实体序列化为json-
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()]);
}
$this->_getSerializer()->normalize($anEntity, 'json');
$this->_getSerializer()->normalize($arrayOfEntities, 'json');
- 如果实体具有循环引用
- 如果要忽略某些属性
- 更好的是,您可以序列化
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;
}
然后,在控制器中编码:
$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));
/**
* Поиск сущности по ИД объекта и ИД языка
* @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 );
}
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()注释可以添加到简单字段和任何类型的关系中。还有可能公开某些方法执行的结果(为此使用annotation@JMS\VirtualProperty()
- 通过序列化组,我们可以在不同的情况下控制公开的字段
- 控制器非常简单。Action方法可以直接返回一个实体或实体数组,它们将被自动序列化
- @ApiDoc()允许直接从浏览器测试端点,无需任何REST客户端或JavaScript代码
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上测试过,谢谢,但我有一个玩家实体,它有到游戏实体集合的链接,每个游戏实体都有到在其中玩过的玩家的链接。像这样的。你认为GetSetMethodNormalizer会正确工作吗(它使用递归算法)?是的,它是递归的,这是我的问题。因此,对于特定的实体,您可以使用CustomNormalizer及其Normalizable接口