2017-04-18 32 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); 

隨意用訪問整個實體圖表的解決方案來完成答案。那太好了。

相關問題