Как изменить метод для вызова объекта в PreSerializeEvent?

У меня есть сущность Category, которая содержит кучу Assets. У категории есть метод getCount, который возвращает количество активов в ней.

Я использую FosRestBundle и, следовательно, JMSSerializerBundle для сериализации ресурса категории в json. Теперь произошло изменение функции, которое представило различные виды активов, например. external и internal.

Я все еще хочу использовать сериализацию сущностей, и я подумал, что, создав PreSerializeEvent, я действительно смогу это сделать.

Тем не менее, документы для компонента сериализатора неясны, так как соответствующая часть упоминается как do something

Как я вообще должен что-то здесь делать?

Чего я хочу добиться, так это изменить метод, который вызывается, если установлен флаг. Обычным случаем для сериализатора должно быть использование метода getCount, с другой стороны, метода getExternalCountOnly для объекта категории, если сериализуется getCount.

Это действительно возможно сделать здесь или я на неправильном пути?

Смотрите мой CategorySerializationSubscriber:

<?php
namespace Hn\AssetDbBundle\Listener;

use Hn\AssetDbBundle\Entity\Category;
use Hn\UserBundle\Entity\User;
use JMS\Serializer\Annotation\PostSerialize;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;

/**
 * Class CategorySerializationSubscriber
 * @package Hn\AssetDbBundle\Listener
 */

class CategorySerializationSubscriber implements EventSubscriberInterface
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var User $user
     */

    private $user;

    /**
     * @param LoggerInterface $logger
     */

    public function __construct(LoggerInterface $logger, SecurityContextInterface $context)
    {
        $this->user = $context->getToken()->getUser();
        $this->logger = $logger;
        $logger->critical($this->user->getFullNameOrEmail());
    }

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

    /**
     * @param PreSerializeEvent $event
     */

    public function onPreSerialize(PreSerializeEvent $event)
    {
        $category = $this->getCategoryFromEvent($event);

        if (!$category) {
            $this->logger->info('is not a category, aborting');
            return;
        }

        $userCanViewInternalAsset = $this->user->isAllowedToViewInternalAssets();

        /**
         * How to actually do something?
         * 
         * Pseudocode follows:
         */

        if ($userCanViewInternalAsset) {
            $seriaization->replaceMethod($category, 'getCount', 'getExternalAssetCount');
        } else {
            /**
             * $category->getCount(); remains intact
             */
        }
    }

    /**
     * @param PreSerializeEvent $event
     * @return Category|null
     */

    protected function getCategoryFromEvent(PreSerializeEvent $event) {

        /**
         * @var Category $category
         */

        $category = $event->getObject();

        if (!$category instanceof Category) {
            return null;

        }

        return $category;

    }
}

person k0pernikus    schedule 23.12.2014    source источник
comment
Что, если вы просто добавите группу JMS к каждой функции — internal для getCount() и external для getExternalAssetCount(), а затем добавите соответствующую группу сериализации на основе уровня доступа пользователя?   -  person Jason Roman    schedule 23.12.2014
comment
@JasonRoman Тогда мне пришлось бы установить группу для всех экземпляров, где я сериализую категорию. Это также неясно из-за зависимостей, поскольку Category также имеет отношения к другим объектам и может быть сериализована при их сериализации. Я скорее хочу избежать этого и сделать так, чтобы это происходило через событие для всех сериализаций.   -  person k0pernikus    schedule 23.12.2014


Ответы (1)


Как насчет исключения по умолчанию, а затем использования подписчика postSerialize() для добавления нужной функции? Например:

Определите в services.yml (или под services в config.yml):

category_serialization_subscriber:
    class: Hn\AssetDbBundle\Listener\CategorySerializationSubscriber
    tags:
        - { name: jms_serializer.event_subscriber }

Затем следующее, которое изменено из вашего класса выше:

public static function getSubscribedEvents()
{
    return array(array(
        'event'  => 'serializer.post_serialize',
        'class'  => 'Hn\AssetDbBundle\Entity\Category',
        'method' => 'onPostSerialize'
    ));
}

public function onPostSerialize(ObjectEvent $event)
{
    $category = $event->getObject();
    $visitor  = $event->getVisitor();

    if ($this->user->isAllowedToViewInternalAssets()) {
        $visitor->addData('count', $category->getExternalAssetCount());
    } else {
        $visitor->addData('count', $category->getCount());
    }
}
person Jason Roman    schedule 23.12.2014
comment
OnPostSerialize не вызывается O.o. Нужен ли другой тег или name: jms_serializer.event_subscriber все еще применяется? - person k0pernikus; 23.12.2014
comment
Я обновил свой пост, чтобы показать, что вам нужно добавить это как прослушиватель событий, чтобы он срабатывал. - person Jason Roman; 23.12.2014
comment
Большое спасибо за вклад, но до сих пор нет кости :( Он даже не вызывает onPostSerialize. Может ли быть проблема с конфигурацией Entity.Cateory.yml? - person k0pernikus; 23.12.2014
comment
Вы можете попробовать удалить часть class из моей конфигурации getSubscribedEvents, чтобы увидеть, срабатывает ли ваш метод onPostSerialize... возможно, добавьте в него $logger->info('test');, чтобы увидеть, срабатывает ли он. - person Jason Roman; 23.12.2014
comment
Подход регистратора - это то, как я узнаю, что событие не запускается. Удаление параметра class также не помогло. EventSubscriber только создается (у меня есть сообщение журнала в его конструкторе), но никогда не вызывается. По крайней мере, был вызван preSerializer. - person k0pernikus; 23.12.2014
comment
Дикое предположение: возможно, существует проблема с сериализацией FosRestBundle, которая может не вызвать событие. - person k0pernikus; 23.12.2014
comment
Я думаю, что дал вам неправильное имя тега, см. правку выше: - { name: jms_serializer.event_subscriber }, а не - { name: jms_serializer.post_serialize } - person Jason Roman; 23.12.2014
comment
Да, в конце концов это произошло. Я столкнулся с проблемой с JmsSerializerBundle или даже с его зависимостью от metadata, когда он пытался получить доступ к прокси-классам Doctrine вместо гидратированных сущностей, которые вызывали исключения в моем случае. Тем не менее, с активной загрузкой ресурсов из объекта «Категория» это сработало, и это достаточное решение для моих нужд. Еще раз большое спасибо за вашу помощь! - person k0pernikus; 06.01.2015
comment
Рад, что у вас все заработало - это очень интересно насчет прокси-классов и гидратированных сущностей с нетерпеливой загрузкой... Я буду иметь это в виду на будущее, если столкнусь с подобными проблемами. - person Jason Roman; 06.01.2015