2017-04-18 65 views
2

我有一个Dashboard实体正确序列化/反序列化由JMSSerializer(通过JMSSerializerBundle):如何更改主义实体的属性的序列化编程

/** 
* @ORM\Table(name="dashboard", schema="myappID") 
* @ORM\Entity(repositoryClass="Belka\MyBundle\Entity\Repository\DashboardRepository") 
*/ 
class Dashboard 
{ 
    /** 
    * @Id 
    * @Column(type="integer") 
    * @GeneratedValue("SEQUENCE") 
    * 
    * @Serializer\Groups({"o-all", "o-all-getCDashboard", "i-p2-editDashboard"}) 
    */ 
    protected $id; 

    /** 
    * @ORM\ManyToMany(targetEntity="Belka\MyBundle\Entity\User") 
    * 
    * @ORM\JoinTable(name="users_dashboards_associated", 
    *  schema="myAppID", 
    *  joinColumns={@ORM\JoinColumn(name="dashboard_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")} 
    *  ) 
    * 
    * @Serializer\groups({ 
    *  "o-p2-create", 
    *  "i-p2-create", 
    *  "o-p2-patch", 
    *  "i-p2-editDashboard" 
    * }) 
    */ 
    protected $users; 
} 

和我使用JMSSerializer的jms_serializer.doctrine_object_constructor为对象的构造函数。 一切都像魅力,但我有以下的角落案件:有时我必须设置Dashboard::$users作为一个字符串(即当客户端发送语义错误users属性,在我检查后,我返回对象与一个字符串,以便通知它,这对前端应用程序来说非常方便)。 JMSSerializer takes advantage of the Doctrine's annotation,但在这种情况下,我真的想以编程方式覆盖它,因为是一个非常特殊的情况。两个都在我的脑海方式:

  1. 是否有为了设置SerializationContext映射Dashboard::$users为一个字符串属性的方法吗?
  2. 有没有一种方法可以在序列化之前改变原则的元数据?
  3. 其他选项我没有意识到?

任何一块的建议是多人欢迎

+1

也许通过[events](http://jmsyst.com/libs/serializer/master/event_system)? – Veve

+1

@Veve我想你是对的。看看我自己的anser :) – Bertuz

回答

2

我已经找到了解决办法,虽然它不考虑嵌套实体的属性(有-A的关系)。这意味着要访问整个图表,但我没有时间研究出色的JMSSSerializer的内核。尽管如此,它完美地用于强制第一级实体的属性:

首先,需要订户pre-serialize。它将循环访问受保护的属性并检查它们是否包含字符串。是的,序列化的类型将被覆盖。

class SerializationSubscriber implements EventSubscriberInterface 
{ 

    /** 
    * @inheritdoc 
    */ 
    static public function getSubscribedEvents() 
    { 
     return array(
      array('event' => 'serializer.pre_serialize', 'method' => 'onPreserialize'), 
     ); 
    } 

    public function onPreSerialize(PreSerializeEvent $event) 
    { 
     $entity = $event->getObject(); 
     $metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($event->getType()['name']); 
     $reflect = new \ReflectionClass($entity); 
     $props = $reflect->getProperties(\ReflectionProperty::IS_PROTECTED); 

     foreach ($props as $prop) { 
      $prop->setAccessible(true); 

      if (is_string($prop->getValue($entity))) { 
       // here is the magic 
       $metadata->propertyMetadata[$prop->name]->type = array('name' => 'string', 'params' => array()); 
      } 
     } 
    } 
} 

接下来,我不想每次我序列化一些东西时听这个。这是我的一项服务中的一个角落案例。我们可以利用JMS\Serializer\EventDispatcher\EventDispatcher::addSubscriber,尽管EventDispatcher服务被宣布为private。 所以,让我们把这一服务为public通过编译通以便采取的addSubscriber优势:

class MyBundle extends Bundle 
{ 
    public function build(ContainerBuilder $container) 
    { 
     parent::build($container); 

     $container->addCompilerPass(new OverrideJmsSerializerEventDispatcherDefPass()); 
    } 
} 

...让我们把这一服务为一体的public一个

class OverrideJmsSerializerEventDispatcherDefPass implements CompilerPassInterface 
{ 
    public function process(ContainerBuilder $container) 
    { 
     $definition = $container->getDefinition('jms_serializer.event_dispatcher'); 
     $definition->setPublic(true); 
    } 
} 

因此,我们可以将其注入我们的服务。即在我services.yml

belka.mybundle.dashboardhandler: 
     class: Belka\MyBundle\Handlers\DashboardHandler 
     calls: 
      - [setEventDispatcher, ["@jms_serializer.event_dispatcher"]] 

好了,现在我们可以轻松地添加我们的用户,只要我们需要,而不必每次我的应用程序进行序列化的另一个听者的负担:

$serializationSubscriber = new SerializationSubscriber(); 
$this->eventDispatcher->addSubscriber($serializationSubscriber); 

随意用访问整个实体图表的解决方案来完成答案。那太好了。

相关问题