Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/246.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/symfony/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 使用Symfony序列化程序的简单REST,不支持反规范化?_Php_Symfony_Doctrine Orm_Json Deserialization - Fatal编程技术网

Php 使用Symfony序列化程序的简单REST,不支持反规范化?

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

我有一个关于在标准Symfony 4.2环境中使用的想法的问题/请求

思考以下两个实体PostAuthor。 每个帖子恰好有一位作者(单向多通关联)

后实体

/**
 * @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(但根据我对REST的理解,应该交换对象的完整表示)
  • 使用GetSetMethodNormalizer而不是ObjectNormalizer(因为没有有效的id设置器,但存在其他一些缺陷)
  • 从非规范化上下文中排除id(例如,通过“忽略的_属性”)
  • 创建自定义规范化器来处理这种情况
  • 现在,让我们尝试从ObjectNormalizer更改为GetSetMethodNormalizer,以克服错误并再次尝试发布

    现在我们得到另一个错误:

    FatalThroTableError:传递给App\Entity\Post::setAuthor()的参数1必须是给定数组的App\Entity\Author实例

    根据GetSetMethodNormalizer的文档,在反规范化中设置值时,不会发生进一步的规范化

    所以我再一次理解这个错误并理解发生了什么

    API平台通过定制

    我理解并欣赏Symfony和Doctrine是两个不同的软件包,但它们经常结合在一起(并且通常一起玩得很精彩):

    Symfony序列化中是否有任何内置或已知的方法,基本上允许我在上述“标准情况”中实现此链运行时不会出现错误,也不会自定义规范化器或其他实现

    数据库=>Doctrine ORM=>Symfony GET object=>json=>POST same json=>Symfony=>Doctrine ORM=>Database

    p.S.:另一个小例子:

    GET/fapi/post/1仅提供

    {"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更新多个不同类型的实体