Php 使用Symfony序列化程序的简单REST,不支持反规范化?
我有一个关于在标准Symfony 4.2环境中使用的想法的问题/请求 思考以下两个实体Post和Author。 每个帖子恰好有一位作者(单向多通关联) 后实体Php 使用Symfony序列化程序的简单REST,不支持反规范化?,php,symfony,doctrine-orm,json-deserialization,Php,Symfony,Doctrine Orm,Json Deserialization,我有一个关于在标准Symfony 4.2环境中使用的想法的问题/请求 思考以下两个实体Post和Author。 每个帖子恰好有一位作者(单向多通关联) 后实体 /** * @ORM\Entity() */ class Post { /** * @var int * * @ORM\Id * @ORM\Column(type="integer", options={"unsigned": true}) * @ORM\GeneratedV
/**
* @ORM\Entity()
*/
class Post
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer", options={"unsigned": true})
* @ORM\GeneratedValue
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="text", length=50)
*/
private $text;
/**
* @var Author
*
* @ORM\ManyToOne(targetEntity="Author", fetch="EAGER")
*/
private $author;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getText(): string
{
return $this->text;
}
/**
* @param string $text
*/
public function setText(string $text): void
{
$this->text = $text;
}
/**
* @return Author
*/
public function getAuthor(): Author
{
return $this->author;
}
/**
* @param Author $author
*/
public function setAuthor(Author $author): void
{
$this->author = $author;
}
}
/**
* @ORM\Entity()
*/
class Author
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer", options={"unsigned": true})
* @ORM\GeneratedValue
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="text", length=50)
*/
private $name;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
}
作者实体
/**
* @ORM\Entity()
*/
class Post
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer", options={"unsigned": true})
* @ORM\GeneratedValue
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="text", length=50)
*/
private $text;
/**
* @var Author
*
* @ORM\ManyToOne(targetEntity="Author", fetch="EAGER")
*/
private $author;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getText(): string
{
return $this->text;
}
/**
* @param string $text
*/
public function setText(string $text): void
{
$this->text = $text;
}
/**
* @return Author
*/
public function getAuthor(): Author
{
return $this->author;
}
/**
* @param Author $author
*/
public function setAuthor(Author $author): void
{
$this->author = $author;
}
}
/**
* @ORM\Entity()
*/
class Author
{
/**
* @var int
*
* @ORM\Id
* @ORM\Column(type="integer", options={"unsigned": true})
* @ORM\GeneratedValue
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="text", length=50)
*/
private $name;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
}
现在,让我们创建一个简单的控制器,它允许获取和发布帖子:
class SimpleController extends AbstractController
{
/**
* @Route("/fapi/post/{id}", methods={"GET", "HEAD"})
* @param int $id
*
* @return Response
*/
public function getEntityAction($id)
{
$em = $this->getDoctrine()->getManager();
$post = $em->getRepository(Post::class)->find($id);
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$serializedPost = $serializer->serialize($post, 'json');
return new Response($serializedPost);
}
/**
* @Route("/fapi/post", methods={"POST"})
*
* @param Request $request
*
* @return Response
*/
public function postEntityAction(Request $request)
{
$data = $request->getContent();
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$post = $serializer->deserialize($data, Post::class, 'json');
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return new Response(''); // return newly created Post
}
}
假设数据库中已经存在Post(id:1)。我们使用GET/fapi/post/1获取它。结果是:
{"id":1,"text":"Hello World!","author":{"id":1,"name":"Huber"}}
这很好,Symfony的ObjectNormalizer负责Post对象的规范化,并将Author对象也包括到结果中
现在,让我们尝试使用post/fapi/post创建一篇新文章,其中已有作者:
现在我们收到Symfony的一个错误:
NotNormalizableValueException:类“App\Entity\Post\”的“id”属性的类型必须是“int\”中的一种(给定“NULL\”)
我理解这一错误并理解它发生的原因,但是否有任何内置的Symfony序列化方式来确保在这个“标准情况”中不考虑自动生成的值(通过ORM、通过像UUID这样的构造函数)
我知道的解决方案:
{"id":1,"text":"Hello World!","author":{"id":1,"name":"Huber"}}
因为ManyToOne关联是即时获取的,所以如果设置了默认的LAZY,那么它将变为:
{"id":1,"text":"Hello World!","author":{"id":1,"name":"Huber","__initializer__":null,"__cloner__":null,"__isInitialized__":true}}
因为ObjectNormalizer序列化了原则代理对象
p.p.S:我知道并经常使用它,但我完全喜欢并支持Symfony拥有“自己的”序列化程序组件。我不明白为什么要发送
“id”:null,因为实体不能有一个值为null的id(尽管对象可能)。你的代理反对序列化是另一回事。谢谢@Jakumi,我明白你的意思。尽管如此,我还是在谈论支持条令实体的Symfony序列化程序。我建议在现有对象上使用反序列化,并保留创建和更新实体的不同路径(顺便说一句)。然而,潜在的问题是您必须以某种方式解决如何处理子实体。我不认为这应该是独立symfony序列化程序的一部分,因为它是非常特定于原则的。我希望您能理解我的意思,(反)序列化程序不负责神奇地猜测正确的对象。如果您为某些对象添加一些回调,以便在仅提供id时实际创建引用(原则可能具有此相关功能),并在不提供id时创建实体。但如果混合的话就很复杂了。我建议不要同时更新多个不同类型的实体。@Jakumi我理解,正如你所看到的,我说的是实体(不感兴趣,例如,用帖子创建新作者等等)——是的,这不应该是标准行为,但是,序列化程序使用元数据来规范化DoctrinNormalizer处理条令对象和关联的方法是一样的,例如:。?看到我想说的了吗?我不明白为什么要发送“id”:null
,因为实体不能有一个值为null的id(尽管对象可能是空的)。你的代理反对序列化是另一回事。谢谢@Jakumi,我明白你的意思。尽管如此,我还是在谈论支持条令实体的Symfony序列化程序。我建议在现有对象上使用反序列化,并保留创建和更新实体的不同路径(顺便说一句)。然而,潜在的问题是您必须以某种方式解决如何处理子实体。我不认为这应该是独立symfony序列化程序的一部分,因为它是非常特定于原则的。我希望您能理解我的意思,(反)序列化程序不负责神奇地猜测正确的对象。如果您为某些对象添加一些回调,以便在仅提供id时实际创建引用(原则可能具有此相关功能),并在不提供id时创建实体。但如果混合的话就很复杂了。我建议不要在sa更新多个不同类型的实体