Deserialize an entity with a relationship with Symfony Serializer Component

前端 未结 5 1786
挽巷
挽巷 2020-12-16 01:27

I\'m trying to deserialize an entity with a relationship using the symfony serializer component. This is my entity:

namespace AppBundle\\Entity;

use Doctrin         


        
5条回答
  •  情歌与酒
    2020-12-16 02:01

    For anyone who is working on this in '18. I've managed to get this working using two different approaches.

    The associated entities I'm working with.

    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;
    }
    

    Method 1: Using Form Classes

    #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);
        }
    }
    

    Method 2: A Custom Normalizer

    The PropertyInfo Component needs to be enabled.

    #/config/packages/framework.yaml
    framework:
        property_info:
            enabled: true
    

    Register the custom normalizer.

    #/config/services.yaml
    services:
        entity_normalizer:
            class: App\SupportClasses\EntityNormalizer
            public: false
            autowire: true
            autoconfigure: true
            tags: [serializer.normalizer]
    

    The custom normalizer.

    #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);
        }
    }
    

    Our controller's create action.

    #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);
        }
    }
    

    This has worked for me. I received inspiration from: https://medium.com/@maartendeboer/using-the-symfony-serializer-with-doctrine-relations-69ecb17e6ebd

    I modified the normalizer to allow me to send the category as a child json object which is converted to a child array when the data is decoded from json. Hopefully this helps someone.

提交回复
热议问题