Сложная сериализация

Например, у меня есть две сущности: основная (родительская) сущность (например, User) и зависимая сущность (Post). Я хочу сериализовать объект User с помощью сериализатора JMS с дополнительной информацией о дате его первой публикации. Дата публикации, хранящаяся в БД, - это временная метка int, но я хочу сериализовать ее с помощью моего помощника (просто службы), который преобразует int в sting с некоторым форматированием.

Попытка создать virtual_property с методом в классе сущности, но не удалось внедрить мой помощник в класс сущности. Единственный способ решить эту проблему для меня - сериализовать себя в контроллер:

public function someAction()
{
    $serializedUser = $this->serializeEntity($user);
}

public function serializeEntity(User $user)
{
    // JMS serialization
    $user = $this->get('jms_serializer')->serialize($user, 'array');

    if ($user->getPosts()->count()) {
        $post = $user->getPosts()->first();
        $user['first_post_date'] = $this->get('my_helper_service')->dateFormat($post->getDate());
    }

    return $user;
}

NB: этот пример синтетический, в реальном мире у меня есть более сложные методы, а не только средство форматирования даты. Но основная идея осталась прежней.

Я чувствую, что должен быть лучший способ сделать это.


person Aliance    schedule 09.01.2017    source источник


Ответы (2)


Решение Dmtry должно идеально работать в вашем случае.

Слушатель / подписчик событий - лучшее решение в этом случае.

Немного более общее решение, которое работает даже с объектами и запускает всю часть системы событий сериализатора JMS (решение Dmtry работает только с примитивами и только для JSON / YAML, а не XML):

class MyFormatter implements EventSubscriberInterface
{

    public static function getSubscribedEvents()
    {
        return array(
            array(
                'event' => 'serializer.post_serialize', 
                'method' => 'onPostSerialize',
                'class' => 'YourEntity'
            ),
        );
    }

    public function __construct(MyFormatter $foermatter)
    {
        $this->formatter = $formatter;
    }

    public function onPostSerialize(ObjectEvent $event)
    {
        $visitor = $event->getVisitor();
        $context = $event->getContext();

        $timestamp = $event->getObject()->getTimestamp();
        $formattedTime = $this->formatter->format($timestamp);

        $metadata = new StaticPropertyMetadata('stdClass', 'first_post_date', $formattedTime);

        $visitor->visitProperty($metadata, $formattedTime, $context);
    }
}

stdClass игнорируется сериализатором ...

person Asmir Mustafic    schedule 09.01.2017

Конечно, есть способ получше. Это называется сериализацией событий.

http://jmsyst.com/libs/serializer/master/event_system

Вы создаете подписчика на мероприятие

my_bundle.serializer_subscriber:
    class: MyBundle\Serializer\MyEntitySerializerSubscriber
    arguments:
        - @bundle.time_formatter
    tags:
        - { name: jms_serializer.event_subscriber }

А затем просто добавьте нужные данные в свой слушатель.

public function myOnPostSerializeMethod(ObjectEvent $event)
{
    if (!($event->getObject() instance of YourEntity)) {
         return;
    }

    $timestamp = $event->getObject()->getTimestamp();

    $visitor = $event->getVisitor();
    $visitor->addData('date', $this->formatter->format($timestamp));
}

P.S. Код не проверял, так что может где-то ошибаюсь с названием методов, но идея понятна, надеюсь

person Dmitry Malyshenko    schedule 09.01.2017
comment
Я думаю о событиях, но будет ли это событие срабатывать при каждой сериализации? Если у меня есть несколько сущностей, которые я хотел бы заполнить, в коде будет много if ($event->getObject() instance of SomeEntity) { $this->someEntityFormat($event); } - person Aliance; 09.01.2017