Сериализовать массив Doctrine, содержащий объекты с использованием наследования

Проблема :

При сериализации коллекции Enctities Doctrine коллекция будет по-прежнему иметь 2 элемента, хотя элементы пустые.

Предпосылки :

У меня есть несколько объектов, которые расширяют друг друга, B расширяет A и C расширяет B В объекте Test меня есть массив с объектами типа B $test будет иметь ожидаемые значения (сбор с двумя элементами) в момент сериализации.

$test содержит collection переменных (массива), один из элементов массива имеет тип B и один тип C

$sTest получит коллекцию из двух элементов, хотя элементы пустые. Так выглядит строка в $sTest после сериализации $test "{"collection":[[],[]]}"

Тестовый скрипт:

 $test = new Test(); $b = new B(); $b->setToken('asdf'); $b->setName('asdf'); $c = new C(); $c->setToken('asdf'); $c->setName('asdf'); $c->setDescription('asdf'); $test->addCollection($b); $test->addCollection($c); //Serialize $serializer = $this->container->get('serializer'); $sTest = $serializer->serialize($test, 'json'); //Deserialize $deserializer = $this->container->get('serializer'); $dTest = $deserializer->deserialize($sTest, 'Acme\DemoBundle\Entity\Test', 'json'); $em = $this->getDoctrine()->getManager(); $em->merge($dTest); $em->flush(); 

A:

 <?php namespace Acme\DemoBundle\Entity; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as JMS; /** * @ORM\Entity * @ORM\InheritanceType("JOINED") * @ORM\DiscriminatorColumn(name="discr", type="string") * @ORM\DiscriminatorMap({"a" = "Acme\DemoBundle\Entity\A", "b" = "Acme\DemoBundle\Entity\B", "c" = "Acme\DemoBundle\Entity\C"}) * * @JMS\ExclusionPolicy("None") * @JMS\Discriminator(field = "type", map = { * "a": "Acme\DemoBundle\Entity\A", * "b": "Acme\DemoBundle\Entity\B", * "c": "Acme\DemoBundle\Entity\C", * * }) */ class A { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\Column(type="string", length=100) */ protected $token; public function setToken($token){ $this->token = $token; } /** * @JMS\VirtualProperty * @JMS\SerializedName("type") */ public function getDiscr() { return 'a'; } } 

B:

 <?php namespace Acme\DemoBundle\Entity; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as JMS; /** * @ORM\Entity * @JMS\ExclusionPolicy("None") */ class B extends A { /** * @ORM\Column(type="string", length=100) */ protected $name; /** * @ORM\ManyToOne(targetEntity="Acme\DemoBundle\Entity\Test", inversedBy="collection") * @ORM\JoinColumn(name="TestId", referencedColumnName="id") */ private $test; public function setName($name) { $this->name = $name; } /** * @JMS\VirtualProperty * @JMS\SerializedName("type") */ public function getDiscr() { return 'b'; } /** * Get name * * @return string */ public function getName() { return $this->name; } /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set token * * @param string $token * @return B */ public function setToken($token) { $this->token = $token; return $this; } /** * Get token * * @return string */ public function getToken() { return $this->token; } /** * Set test * * @param \Acme\DemoBundle\Entity\Test $test * @return B */ public function setTest(\Acme\DemoBundle\Entity\Test $test = null) { $this->test = $test; return $this; } /** * Get test * * @return \Acme\DemoBundle\Entity\Test */ public function getTest() { return $this->test; } } 

C:

 <?php namespace Acme\DemoBundle\Entity; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as JMS; /** * @ORM\Entity * @JMS\ExclusionPolicy("None") */ class C extends B { /** * @ORM\Column(type="text") */ protected $description; public function setDescription($description) { $this->description = $description; } /** * @JMS\VirtualProperty * @JMS\SerializedName("type") */ public function getDiscr() { return 'c'; } } 

Контрольная работа:

 <?php namespace Acme\DemoBundle\Entity; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as JMS; /** * @ORM\Entity */ class Test { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\OneToMany(targetEntity="Acme\DemoBundle\Entity\B", mappedBy="test", cascade={"all"}) * @JMS\Type("ArrayCollection<'Acme\DemoBundle\Entity\B'>") */ private $collection; /** * Constructor */ public function __construct() { $this->collection = new \Doctrine\Common\Collections\ArrayCollection(); } /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Add collection * * @param \Acme\DemoBundle\Entity\B $collection * @return Test */ public function addCollection(\Acme\DemoBundle\Entity\B $collection) { $this->collection[] = $collection; return $this; } /** * Remove collection * * @param \Acme\DemoBundle\Entity\B $collection */ public function removeCollection(\Acme\DemoBundle\Entity\B $collection) { $this->collection->removeElement($collection); } /** * Get collection * * @return \Doctrine\Common\Collections\Collection */ public function getCollection() { return $this->collection; } } 

Неправильная аннотация для Test::$collection

Как указано NDM , аннотация для Test::$collection неверна, вам нужно опустить кавычки при ссылке на тип:

 diff --git a/src/Test.php b/src/Test.php index c0da0c3..a5ea94e 100644 --- a/src/Test.php +++ b/src/Test.php @@ -19,7 +19,7 @@ class Test { /** * @ORM\OneToMany(targetEntity="Acme\DemoBundle\Entity\B", mappedBy="test", cascade={"all"}) - * @JMS\Type("ArrayCollection<'Acme\DemoBundle\Entity\B'>") + * @JMS\Type("ArrayCollection<Acme\DemoBundle\Entity\B>") */ private $collection; 

Для справки см. http://jmsyst.com/libs/serializer/master/reference/annotations#type .

Отсутствующие аннотации для A::$token и B::$name

Попытка сериализации после фиксации аннотации для Test::$collection приводит к следующим исключениям:

 JMS\Serializer\Exception\RuntimeException: You must define a type for Acme\DemoBundle\Entity\B::$name. 

а также

 JMS\Serializer\Exception\RuntimeException: You must define a type for Acme\DemoBundle\Entity\A::$token. 

Добавление отсутствующей аннотации для A::$token :

 diff --git a/src/A.php b/src/A.php index eb89b36..f806581 100644 --- a/src/A.php +++ b/src/A.php @@ -29,6 +29,7 @@ class A { /** * @ORM\Column(type="string", length=100) + * @JMS\Type("string") */ protected $token; 

и для B::$name :

 diff --git a/src/B.php b/src/B.php index 71a8b0b..7b448c6 100644 --- a/src/B.php +++ b/src/B.php @@ -13,6 +13,7 @@ class B extends A { /** * @ORM\Column(type="string", length=100) + * @JMS\Type("string") */ protected $name; 

решает проблему и дает ваш скрипт сверху, $test может быть успешно сериализован

 { "collection":[ { "type":"b", "token":"asdf", "name":"asdf" }, { "type":"c", "token":"asdf", "name":"asdf", "description":"asdf" } ] } 

Для несколько иного требования (JSON) я создал базовый объект и выполнил следующее поведение:

 function jsonSerialize() { $arr = get_object_vars($this); foreach ($arr as $key => $var) { unset($arr[$key]); if (method_exists($var, 'jsonSerialize')) { $var = $var->jsonSerialize(); } elseif ( $var instanceof \DateTime) { $var = $var->format('Ymd\TH:i:s\Z'); } $arr[strtolower(preg_replace('/([az])([AZ])/', '$1_$2', $key))] = $var; } return $arr; } с function jsonSerialize() { $arr = get_object_vars($this); foreach ($arr as $key => $var) { unset($arr[$key]); if (method_exists($var, 'jsonSerialize')) { $var = $var->jsonSerialize(); } elseif ( $var instanceof \DateTime) { $var = $var->format('Ymd\TH:i:s\Z'); } $arr[strtolower(preg_replace('/([az])([AZ])/', '$1_$2', $key))] = $var; } return $arr; }