【问题标题】:Add dynamic property on entity to be serialized在要序列化的实体上添加动态属性
【发布时间】:2015-11-13 12:21:24
【问题描述】:

我有这个 REST API。每当请求通过 id (/resource/{id}) 获取资源时,我想在该对象上动态添加一个 permissions 数组(实体本身没有该字段)。

我想出的是这个事件监听器。它检查控制器返回的结果:

class PermissionFinderListener {
    ...

    public function onKernelView(GetResponseForControllerResultEvent $event)  {
        $object = $event->getControllerResult();

        if (!is_object($object) || !$this->isSupportedClass($object)) {
            return;
        }

        $permissions = $this->permissionFinder->getPermissions($object);
        $object->permissions = $permissions;

        $event->setControllerResult($object);
    }
    ....
}

问题在于 JMS 序列化器在序列化时选择退出此动态属性。我尝试在 JMS 序列化程序上创建 onPostSerialize 事件订阅者,但是没有明确的方法来检查这是 GET ONE 还是 GET COLLECTION 请求。我不需要GET COLLECTION 上的这种行为,而且它也会对集合序列化造成巨大的性能影响。此外,我不想使用permission 属性创建任何基实体类。

也许还有其他方法可以处理这种情况?

【问题讨论】:

    标签: symfony jms-serializer


    【解决方案1】:

    我能想象的是虚拟属性和序列化组的组合:

    向您的实体添加一个属性,例如:

     /**
     * @Serializer\VirtualProperty
     * @Serializer\SerializedName("permissions")
     * @Serializer\Groups({"includePermissions"}) */
     *
     * @return string
     */
    public function getPermissions()
    {
        return $permissionFinder->getPermissions($this);
    }
    

    您唯一需要做的就是仅在您的特殊情况下序列化“includePermissions”组(请参阅http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies

    如果您无权从您的实体访问 $permissionFinder,您也可以在序列化之前从 Controller/Service 设置实体的权限属性。

    编辑

    通过包装您的实体并将 VirtualProperty 与 SerializationGroups 一起使用,这是一个更多代码来演示我的意思。这段代码根本没有经过测试——它基本上是我们正在使用的手动复制和剥离的版本。所以请把它当作一个想法!

    1) 为您的实体创建类似于包装类的东西:

     <?php
     namespace Acquaim\ArcticBundle\Api;
    
     use JMS\Serializer\Annotation as JMS;
    
     /**
      * Class MyEntityApi
      *
      * @package My\Package\Api
      */
      class MyEntityApi
    {
    /**
     * The entity which is wrapped
     *
     * @var MyEntity
     * @JMS\Include()
     */
    protected $entity;
    
    protected $permissions;
    /**
     * @param MyEntity     $entity
     * @param Permission[] $permissions
     */
    public function __construct(
        MyEntity $entity,
        $permissions = null)
    {
        $this->entity = $entity;
        $this->permissions = $permissions;
    }
    
    /**
     * @Serializer\VirtualProperty
     * @Serializer\SerializedName("permissions")
     * @Serializer\Groups({"includePermissions"})
     *
     * @return string
     */
    public function getPermissions()
    {
        if ($this->permissions !== null && count($this->permissions) > 0) {
            return $this->permissions;
        } else {
            return null;
        }
    }
    
    /**
     * @return object
     */
    public function getEntity()
    {
        return $this->entity;
    }
    
    }
    

    2) 在您的控制器中不要返回您的原始实体,而是获取您的权限并使用实体和权限创建您的包装类。 设置您的序列化上下文以包含权限并让 ViewHandler 返回您的序列化对象。

    如果您没有将序列化上下文设置为 includePermissions,它将被排除在序列化结果之外。

     YourController:
    
     $myEntity = new Entity();
    
     $permissions = $this->get('permission_service')->getPermissions();
    
     $context =   SerializationContext::create()->setGroups(array('includePermissions'));
    
     $myEntityApi = new MyEntityApi($myEntity,$permissions);
    
     $view = $this->view($myEntityApi, 200);
    
     $view->setSerializationContext($context);
    
     return $this->handleView($view);
    

    【讨论】:

    • 我希望避免更改或耦合我的实体
    • 如何将您的实体包装在具有虚拟属性的实体中?例如通过扩展您的 ViewHandler 或类似的东西?那么你就不需要使用事件监听器了。
    • 也许你可以放一些你所建议的代码示例
    • @nn4n4s : 示例添加,请检查
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-04
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    • 1970-01-01
    相关资源
    最近更新 更多