dongpan1871 2016-04-20 01:09
浏览 24
已采纳

JMSSerializerBundle与抽象类 - Symfony2

I want to serialize and deserialize an entity with its dependancies but I am unable to serialize elements that concern an abstract class.

Hierarchy :

Test --> several Calls where Call class is an abstract class and is extended by TestCallExecuteQuery (same issue with $conditions)

Test.php :

/**
 * @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
 * @ORM\Table(name="cfa_test")
 * @JMSSer\ExclusionPolicy("all")
 */
class Test
{

    /**
     * @ORM\OneToMany(targetEntity="TestCall", mappedBy="test", cascade={"all"}, orphanRemoval=true)
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     * @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
     */
    protected $calls;

    /**
     * @ORM\OneToMany(targetEntity="TestCondition", mappedBy="test", cascade={"all"}, orphanRemoval=true)
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     * @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCondition>")
     */
    protected $conditions;

TestCall.php :

/**
 * @ORM\Entity
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\Table(name="cfa_test_call")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({
 *      "executeQuery" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
 *      "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
 * })
 * @JMSSer\ExclusionPolicy("all")
 * @JMSSer\Discriminator(field="serializedType", map={
 *      "executeQuery"="App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
 *      "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
 * })
 */
abstract class TestCall
{
    /**
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     */
    protected $type = 'call';

    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="Test", inversedBy="calls")
     */
    protected $test;
  /**
     * @JMSSer\VirtualProperty()
     * @JMSSer\SerializedName("serializedType")
     */
    public function getDiscr()
    {
        return $this->type;
    }

TestCallExecuteQuery.php :

/**
 * @ORM\Entity
 * @JMSSer\ExclusionPolicy("all")
 */
class TestCallExecuteQuery extends TestCall
{

    protected $type = 'executeQuery';

    /**
     * @ORM\Column(name="`query`", type="text")
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     */
    protected $query;

    /**
     * @ORM\Column(name="`return`", type="string", nullable=true)
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     */
    protected $return;

So I followed instructions found over Internet :

  • @JMSSer\Expose annotation with @JMSSer\ExclusionPolicy("all") in every class
  • @JMSSer\Discriminator annotation on top of abstract class TestCall to map with extender class (TestcallExecuteQuery)

But.. When I serialize I get only type property of TestCall but not query or return property defined in TestCallExecuteQuery :

{"tests":[{"calls":[{"type":"executeQuery"},{"type":"executeQuery"}], ... }

I know it's possible because I got them ONCE but I was unable to reproduce that even by turning back the clock..

{"tests":[{"calls":[{"query":"SELECT * FROM table","return":"return_1"}], ... }

EDIT :

Ok I probably got query and return by changing in Test.php :

/**
 * @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
 */
    protected $calls;

To :

/**
 * @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery>")
 */
    protected $calls;

What am I doing wrong ?

展开全部

  • 写回答

1条回答 默认 最新

  • douyin4875 2016-04-21 02:12
    关注

    Ok so ! I found a solution after losing my mind during several days !

    The solution is to create two event listeners PreSerialize and PostSerialize.

    First of all I removed this part in TestCall.php (Abstract Class) :

    /**
     * @JMSSer\VirtualProperty()
     * @JMSSer\SerializedName("serializedType")
     */
    public function getDiscr()
    {
        return $this->type;
    }
    

    And added these annotations in TestCallExecuteQuery.php (extender class) :

    /**
     * @JMSSer\Type("string")
     * @JMSSer\Expose
     * @JMSSer\Groups({"export"})
     */
    protected $type = 'executeQuery';
    

    My listener looks like that :

    <?php
    
    namespace App\Bundle\CapFileAnalyzerBundle\EventListener;
    
    use JMS\Serializer\EventDispatcher\Events;
    use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
    use JMS\Serializer\EventDispatcher\ObjectEvent;
    use JMS\Serializer\EventDispatcher\PreSerializeEvent;
    
    class JMSSerializerListener implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                ['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize'],
                ['event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialize']
            ];
        }
    
        /**
         * @param PreSerializeEvent $event
         */
        public function onPreSerialize(PreSerializeEvent $event)
        {
            $object = $event->getObject();
    
            if (is_object($object) &&
                is_subclass_of($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCall') &&
                get_class($object) !== $event->getType()['name']
            ) {
                $event->setType(get_class($event->getObject()));
            }
        }
    
        /**
         * @param ObjectEvent $event
         */
        public function onPostSerialize(ObjectEvent $event){
            $object = $event->getObject();
            if (is_object($object) &&
                is_a($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery')) {
                $event->getVisitor()->addData("serializedType", $object->getType());
            }
        }
    }
    

    Declaration of listener :

    <?xml version="1.0" ?>
    
    <container xmlns="http://symfony.com/schema/dic/services"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <parameters>      
            <parameter key="cfa.events.jmsserializer_listener.class">App\Bundle\CapFileAnalyzerBundle\EventListener\JMSSerializerListener</parameter>
        </parameters>
    
        <services>
             <service id="cfa.events.jmsserializer_listener" class="%cfa.events.jmsserializer_listener.class%">
                <tag name="jms_serializer.event_subscriber"/>
            </service>
    
        </services>
    </container>
    

    I explain a little :

    1. PreSerailize Event

    If object to serialize is a subclass of my Abstract class (TestCall in my case) I have to force the type of the event object to be serialized into the concerned subclass (TestCallExecuteQuery in my case). In fact, right object (TestCallExecuteQuery) is passed but it's mapped with its parent class (Abstract class TestCall)

    Dump of $event object :

    PreSerializeEvent {#977 ▼
      -object: TestCallExecuteQuery {#981 ▼
        #type: "executeQuery"
        #query: "SELECT * FROM table_name"
        #return: "return_3"
        #id: 2
        #test: Test {#948 ▶}
      }
      #type: array:2 [▼
        "name" => "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
        "params" => []
      ]
      -context: SerializationContext {#420 ▶}
    }
    
    1. PostSerialze Event

    If serialized object is my child class I add a visitor property (not a virtual even custom)..

    NB : JMSSerializeBundle does not serialize "virtual" property directly added to class for example by a method like that :

    public function createProperty($name, $value) {    
         $this->{$name} = $value;    
    }
    

    Maybe Discriminator/Virtual Property are added like that in JMSSerializerBundle and that's why they are not serialized.. Don't really know.

    展开全部

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
编辑
预览

报告相同问题?

手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部