Symfony 4序列化没有关系的实体
Symfony 4 serialize entity without relations
我必须记录每个实体的变化。我有监听器,它监听 preRemove
、postUpdate
和 postDelete
上的学说事件。
我的实体 AccessModule
有关系:
App\Entity\AccessModule.php
/**
* @ORM\OneToMany(targetEntity="App\Entity\AccessModule", mappedBy="parent")
* @ORM\OrderBy({"id" = "ASC"})
*/
private $children;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\AccessModule", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
private $parent;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\AccessModuleRoute", inversedBy="access_modules")
* @ORM\JoinTable(name="access_routes",
* joinColumns={@ORM\JoinColumn(name="access_module_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="route_id", referencedColumnName="id")})
*
*/
private $routes;
在听众中:
App\EventListener\EntityListener.php
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
$this->serializer = new Serializer([$normalizer], $encoders);
public function createLog(LifecycleEventArgs $args, $action){
$em = $args->getEntityManager();
$entity = $args->getEntity();
if ($this->tokenStorage->getToken()->getUser()) {
$username = $this->tokenStorage->getToken()->getUser()->getUsername();
} else {
$username = 'anon'; // TODO Remove anon. set null value
}
$log = new Log();
// $log->setData('dddd')
$log->setData($this->serializer->serialize($entity, ''json)
->setAction($action)
->setActionTime(new \DateTime())
->setUser($username)
->setEntityClass(get_class($entity));
$em->persist($log);
$em->flush();
}
我遇到序列化问题
当我使用 $log->setData($entity)
时,我遇到了 Circular 的问题。
当我进行连载 $log->setData($this->serializer->serialize($entity, ''json)
时,我充满了关系,与 parent 的 children,与 children children。结果我得到了完整的树:/
我想得到
预计
[
'id' => ID,
'name' => NAME,
'parent' => parent_id // ManyToOne, I'd like get its id
'children' => [$child_id, $child_id, $child_id] // array of $id of children array collection
]
(当然这是编码为json之前的草稿)
如何在没有完整关系的情况下获得预期数据?
现在让我解释一下它是如何工作的。这很简单。假设您有 Post 个实体:
class Post
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"default"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User\User")
* @Groups({"default"})
*/
private $author;
}
你还有用户实体:
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"default"})
*/
private $id;
/**
* @ORM\Column(type="string", length=40)
* @Groups({"default"})
*/
private $firstName;
/**
* @ORM\Column(type="string", length=40)
*/
private $lastName;
}
Post 可以有作者(用户),但我不想每次都 return 所有用户数据。我只对 id 和名字感兴趣。
仔细查看 @Groups 注释。您可以指定所谓的序列化组。它只不过是告诉 Symfony 您希望在结果集中包含哪些数据的便捷方式。
你必须告诉 Symfony 序列化程序你想通过在上面添加注释形式的相关组来保持哪些关系 property/getter。您还必须指定要保留的关系的哪些属性或 getter。
现在如何让 Symfony 知道这些东西?
当您 prepare/configure 您的序列化服务时,您只需简单地提供这样定义的组:
return $this->serializer->serialize($data, 'json', ['groups' => ['default']]);
围绕本机 symfony 序列化器构建某种包装服务是很好的,这样您可以简化整个过程并使其更易于重用。
还要确保序列化器配置正确——否则它不会考虑这些组。
这也是 "handling" 循环引用的一种方式(除其他方式外)。
现在您只需要研究如何格式化结果集。
在 Symfony 4.1 中测试,这里是实际工作的文档https://symfony.com/blog/new-in-symfony-2-7-serialization-groups
Robert 的解释 缺少 $classMetadataFactory 才能正常工作。这是我的代码:
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$encoders = [new JsonEncoder()];
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setCircularReferenceLimit(2);
// Add Circular reference handler
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
$normalizers = [$normalizer];
$serializer = new Serializer($normalizers, $encoders);
$jsonContent = $serializer->serialize($jobs, 'json', array('groups' => ['default']));
return JsonResponse::fromJsonString($jsonContent);
ignored_attributes
提供了一种快速、简单的方法来完成您正在寻找的东西。
$serializer->serialize($object, 'json', ['ignored_attributes' => ['ignored_property']]);
下面是它如何与序列化程序一起使用:
use Acme\Person;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
$person = new Person();
$person->setName('foo');
$person->setAge(99);
$normalizer = new ObjectNormalizer();
$encoder = new JsonEncoder();
$serializer = new Serializer([$normalizer], [$encoder]);
$serializer->serialize($person, 'json', ['ignored_attributes' => ['age']]);
文档:https://symfony.com/doc/current/components/serializer.html#ignoring-attributes
我必须记录每个实体的变化。我有监听器,它监听 preRemove
、postUpdate
和 postDelete
上的学说事件。
我的实体 AccessModule
有关系:
App\Entity\AccessModule.php
/**
* @ORM\OneToMany(targetEntity="App\Entity\AccessModule", mappedBy="parent")
* @ORM\OrderBy({"id" = "ASC"})
*/
private $children;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\AccessModule", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
private $parent;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\AccessModuleRoute", inversedBy="access_modules")
* @ORM\JoinTable(name="access_routes",
* joinColumns={@ORM\JoinColumn(name="access_module_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="route_id", referencedColumnName="id")})
*
*/
private $routes;
在听众中: App\EventListener\EntityListener.php
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
$this->serializer = new Serializer([$normalizer], $encoders);
public function createLog(LifecycleEventArgs $args, $action){
$em = $args->getEntityManager();
$entity = $args->getEntity();
if ($this->tokenStorage->getToken()->getUser()) {
$username = $this->tokenStorage->getToken()->getUser()->getUsername();
} else {
$username = 'anon'; // TODO Remove anon. set null value
}
$log = new Log();
// $log->setData('dddd')
$log->setData($this->serializer->serialize($entity, ''json)
->setAction($action)
->setActionTime(new \DateTime())
->setUser($username)
->setEntityClass(get_class($entity));
$em->persist($log);
$em->flush();
}
我遇到序列化问题
当我使用 $log->setData($entity)
时,我遇到了 Circular 的问题。
当我进行连载 $log->setData($this->serializer->serialize($entity, ''json)
时,我充满了关系,与 parent 的 children,与 children children。结果我得到了完整的树:/
我想得到
预计
[
'id' => ID,
'name' => NAME,
'parent' => parent_id // ManyToOne, I'd like get its id
'children' => [$child_id, $child_id, $child_id] // array of $id of children array collection
]
(当然这是编码为json之前的草稿)
如何在没有完整关系的情况下获得预期数据?
现在让我解释一下它是如何工作的。这很简单。假设您有 Post 个实体:
class Post
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"default"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User\User")
* @Groups({"default"})
*/
private $author;
}
你还有用户实体:
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"default"})
*/
private $id;
/**
* @ORM\Column(type="string", length=40)
* @Groups({"default"})
*/
private $firstName;
/**
* @ORM\Column(type="string", length=40)
*/
private $lastName;
}
Post 可以有作者(用户),但我不想每次都 return 所有用户数据。我只对 id 和名字感兴趣。
仔细查看 @Groups 注释。您可以指定所谓的序列化组。它只不过是告诉 Symfony 您希望在结果集中包含哪些数据的便捷方式。
你必须告诉 Symfony 序列化程序你想通过在上面添加注释形式的相关组来保持哪些关系 property/getter。您还必须指定要保留的关系的哪些属性或 getter。
现在如何让 Symfony 知道这些东西?
当您 prepare/configure 您的序列化服务时,您只需简单地提供这样定义的组:
return $this->serializer->serialize($data, 'json', ['groups' => ['default']]);
围绕本机 symfony 序列化器构建某种包装服务是很好的,这样您可以简化整个过程并使其更易于重用。
还要确保序列化器配置正确——否则它不会考虑这些组。
这也是 "handling" 循环引用的一种方式(除其他方式外)。
现在您只需要研究如何格式化结果集。
在 Symfony 4.1 中测试,这里是实际工作的文档https://symfony.com/blog/new-in-symfony-2-7-serialization-groups
Robert 的解释
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$encoders = [new JsonEncoder()];
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setCircularReferenceLimit(2);
// Add Circular reference handler
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
$normalizers = [$normalizer];
$serializer = new Serializer($normalizers, $encoders);
$jsonContent = $serializer->serialize($jobs, 'json', array('groups' => ['default']));
return JsonResponse::fromJsonString($jsonContent);
ignored_attributes
提供了一种快速、简单的方法来完成您正在寻找的东西。
$serializer->serialize($object, 'json', ['ignored_attributes' => ['ignored_property']]);
下面是它如何与序列化程序一起使用:
use Acme\Person;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
$person = new Person();
$person->setName('foo');
$person->setAge(99);
$normalizer = new ObjectNormalizer();
$encoder = new JsonEncoder();
$serializer = new Serializer([$normalizer], [$encoder]);
$serializer->serialize($person, 'json', ['ignored_attributes' => ['age']]);
文档:https://symfony.com/doc/current/components/serializer.html#ignoring-attributes