反序列化与Symfony Serializer Component关系的实体

dac*_*una 12 json symfony doctrine-orm deserialization

我正在尝试使用symfony序列化程序组件对具有关系的实体进行反序列化.这是我的实体:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Document
 *
 * @ORM\Table(name="document")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\DocumentRepository")
 */
class Document
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Genre", inversedBy="documents")
     * @ORM\JoinColumn(name="id_genre", referencedColumnName="id")
     */
    private $genre;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=100)
     */
    private $name;

    //getters and setters down here
    ...
}
Run Code Online (Sandbox Code Playgroud)

类型实体:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Genre
 *
 * @ORM\Table(name="genre")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\GenreRepository")
 */
class Genre
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=50, nullable=true)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="Document", mappedBy="genre")
     */
    private $documents;

    public function __construct()
    {
        $this->documents= new ArrayCollection();
    }

    //getters and setters down here
    ....
}
Run Code Online (Sandbox Code Playgroud)

在我的控制器动作中,我正在尝试这个:

$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);

$document = $serializer->deserialize($request->getContent(), 'AppBundle\Entity\Document', 'json');
Run Code Online (Sandbox Code Playgroud)

我的json数据:

{"name": "My document", "genre": {"id": 1, "name": "My genre"}}
Run Code Online (Sandbox Code Playgroud)

但我得到了下一个错误:

给出的类型为"AppBundle\Entity\Genre","数组"的预期参数(500内部服务器错误)

是否可以使用内部关系的实体反序列化json请求?

谢谢你的推荐.

Thé*_*héo 9

是的,不是.首先,您不应该在控制器中重新创建序列化程序的新实例,而是使用该serializer服务.

其次,不能用Symfony序列化器开箱即用.我们是在https://api-platform.com/中进行的,但那里有一些魔力.也就是说,已经制定了PR来支持它:https://github.com/symfony/symfony/pull/19277


小智 5

对于在 18 年从事此工作的任何人。我已经设法使用两种不同的方法来完成这项工作。

我正在使用的关联实体。

class Category
{
     /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", name="name", length=45, unique=true)
     */
    private $name;
}

class Item
{
     /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", name="uuid", length=36, unique=true)
     */
    private $uuid;

    /**
     * @ORM\Column(type="string", name="name", length=100)
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Category", fetch="EAGER")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
     */
    private $category;
}
Run Code Online (Sandbox Code Playgroud)

方法一:使用表单类

#ItemType.php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use App\Entity\Category;
use App\Entity\Item;

class ItemType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('category', EntityType::class, [
                'class' => Category::class,
                'choice_label' => 'name',
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Item::class,
        ));
    }
}

#ItemController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use App\Entity\Item;
use App\Form\ItemType;

class ItemController extends BaseEntityController
{
    protected $entityClass = Item::class;

    /**
     * @Route("/items", methods="POST")
     */
    public function createAction(Request $request)
    {
        $data = $request->getContent();
        $item = new Item();
        $form = $this->createForm(ItemType::class, $item);
        $decoded = $this->get('serializer')->decode($data, 'json');
        $form->submit($decoded);

        $object = $form->getData();

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($object);
        $entityManager->flush();

        return $this->generateDataResponse("response text", 201);
    }
}
Run Code Online (Sandbox Code Playgroud)

方法 2:自定义规范器

需要启用 PropertyInfo 组件。

#/config/packages/framework.yaml
framework:
    property_info:
        enabled: true
Run Code Online (Sandbox Code Playgroud)

注册自定义规范器。

#/config/services.yaml
services:
    entity_normalizer:
        class: App\SupportClasses\EntityNormalizer
        public: false
        autowire: true
        autoconfigure: true
        tags: [serializer.normalizer]
Run Code Online (Sandbox Code Playgroud)

自定义规范器。

#EntityNormalizer.php
namespace App\SupportClasses;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;


class EntityNormalizer extends ObjectNormalizer
{
    protected $entityManager;

    public function __construct(
        EntityManagerInterface $entityManager,
        ?ClassMetadataFactoryInterface $classMetadataFactory = null,
        ?NameConverterInterface $nameConverter = null,
        ?PropertyAccessorInterface $propertyAccessor = null,
        ?PropertyTypeExtractorInterface $propertyTypeExtractor = null
    ) {
        $this->entityManager = $entityManager;

        parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return (strpos($type, 'App\\Entity\\') === 0) && 
        (is_numeric($data) || is_string($data) || (is_array($data) && isset($data['id'])));
    }

    public function denormalize($data, $class, $format = null, array $context = [])
    {
        return $this->entityManager->find($class, $data);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们的控制器的创建操作。

#ItemController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use App\Entity\Item;
use App\Form\ItemType;

class ItemController extends BaseEntityController
{
    protected $entityClass = Item::class;

    /**
     * @Route("/items", methods="POST")
     */
    public function createAction(Request $request)
    {
        $data = $request->getContent();
        $object = $this->get('serializer')->deserialize($data, $this->entityClass, 'json');

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($object);
        $entityManager->flush();

        return $this->generateDataResponse('response text', 201);
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我有用。我的灵感来自:https : //medium.com/@maartendeboer/using-the-symfony-serializer-with-doctrine-relations-69ecb17e6ebd

我修改了规范化器,以允许我将类别作为子 json 对象发送,当从 json 解码数据时,该对象将转换为子数组。希望这有助于某人。

  • 我建议不要扩展“ObjectNormalizer”。通过这样做,您将创建另一个“ObjectNormalizer”,它将对所有对象应用通用数组非规范化,并且具有比默认规范化器更高的优先级,这可能会导致“DateTime”等特定对象出现问题。如果您不打算覆盖标准化部分,只需让您的“EntityNormalizer”(实际上只是一个 _de_normalizer)实现“DenormalizerInterface”。这就是您所需要的,这样就不会干扰其他标准化器。 (2认同)