Добавить динамическое свойство для сериализуемой сущности

У меня есть этот REST API. Всякий раз, когда приходит запрос на получение ресурса по идентификатору (/ 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 отключает это динамическое свойство при сериализации. Я попытался создать подписчика событий onPostSerialize в сериализаторе JMS, но тогда нет четкого способа проверить, является ли это запросом GET ONE или GET COLLECTION. Мне не нужно такое поведение на GET COLLECTION, а также оно приводит к огромному снижению производительности при сериализации коллекции. Также я не хочу создавать какой-либо базовый класс сущности со свойством permission.

Может, есть другой способ справиться с этим сценарием?


person andrius.k    schedule 13.11.2015    source источник


Ответы (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 от вашего объекта, вы также можете установить атрибут разрешения объекта из контроллера / службы перед его сериализацией.

ИЗМЕНИТЬ:

Это немного больше кода, чтобы продемонстрировать, что я имею в виду, оборачивая вашу сущность и используя 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);
person LBA    schedule 13.11.2015
comment
Я бы хотел, чтобы мои объекты не изменялись и не связывались - person andrius.k; 13.11.2015
comment
как насчет того, чтобы обернуть вашу сущность в ту, которая имеет виртуальное свойство? например расширяя свой ViewHandler или что-то подобное? тогда вам не нужно играть со слушателем событий. - person LBA; 13.11.2015
comment
Может быть, вы можете привести пример кода, который вы предлагаете - person andrius.k; 15.11.2015
comment
@ nn4n4s: пример добавлен, проверьте - person LBA; 17.11.2015