【问题标题】:JMS VirtualProperty with argument and custom listener/subscriber带有参数和自定义侦听器/订阅者的 JMS VirtualProperty
【发布时间】:2017-08-01 15:06:46
【问题描述】:

我正在尝试序列化作为 Doctrine Criteria 的属性:

public function getUserResults(User $user)
{
    $criteria = Criteria::create()
        ->where(Criteria::expr()->eq('user', $user))
    ;

    return $this->getResults()->matching($criteria);
}

我不能使用@VirtualProperty,因为它需要一个参数,所以我在这篇文章之后为我的一种类型实现了一个自定义订阅者:

https://stackoverflow.com/a/44244747

<?php

namespace AppBundle\Serializer;

use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use JMS\Serializer\EventDispatcher\ObjectEvent;

class ExerciseSubscriber implements EventSubscriberInterface
{
    private $currentUser;

    public function __construct(TokenStorage $tokenStorage)
    {
        $this->currentUser = $tokenStorage->getToken()->getUser();
    }

    public static function getSubscribedEvents()
    {
        return array(
            array(
                'event' => 'serializer.post_serialize',
                'method' => 'onPostSerialize',
                'class' => Exercise::class, // if no class, subscribe to every serialization
                'format' => 'json', // optional format
            ),
        );
    }

    public function onPostSerialize(ObjectEvent $event)
    {
        if (!$this->currentUser) {
            return;
        }

        $exercise = $event->getObject();
        $visitor = $event->getVisitor();

        $results = $exercise->getUserResults($this->currentUser);
        dump($results); // <-- It is an ArrayCollection with many elements

        $visitor->setData(
            'my_user_results',
            $results // <-- when rendered is an empty {}
        );
    }
}

很遗憾,user_results 属性始终为空!
我查看了序列化程序的源代码,发现:

/**
 * Allows you to add additional data to the current object/root element.
 * @deprecated use setData instead
 * @param string $key
 * @param integer|float|boolean|string|array|null $value This value must either be a regular scalar, or an array.
 *                                                       It must not contain any objects anymore.
 */
public function addData($key, $value)
{
    if (isset($this->data[$key])) {
        throw new InvalidArgumentException(sprintf('There is already data for "%s".', $key));
    }

    $this->data[$key] = $value;
}

请注意它不能再包含任何对象。

我该如何解决这个问题?

【问题讨论】:

    标签: symfony doctrine jms-serializer


    【解决方案1】:

    请尝试使用serializer.post_serialize 事件而不是serializer.pre_serialize 事件。此外,您的虚拟属性名称 (user_results) 应该不同于任何现有的可序列化字段。

    setData 方法的第二个参数必须是常规标量或数组。它不能包含任何对象。我建议将 JMS 序列化程序注入到您的侦听器类中,以将您的对象数组序列化为标量数组。

    <?php
    
    namespace AppBundle\Serializer;
    
    use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
    use JMS\Serializer\EventDispatcher\PreSerializeEvent;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
    use JMS\Serializer\EventDispatcher\ObjectEvent;
    use JMS\Serializer\Serializer;
    
    class ExerciseSubscriber implements EventSubscriberInterface
    {
        private $currentUser;
        private $serializer;
    
        public function __construct(TokenStorage $tokenStorage, Serializer $serializer)
        {
            $this->currentUser = $tokenStorage->getToken()->getUser();
            $this->serializer  = $serializer;
        }
    
        public static function getSubscribedEvents()
        {
            return array(
                array(
                    'event'  => 'serializer.post_serialize',
                    'method' => 'onPostSerialize',
                    'class'  => Exercise::class, // if no class, subscribe to every serialization
                    'format' => 'json', // optional format
                ),
            );
        }
    
        public function onPostSerialize(ObjectEvent $event)
        {
            if (!$this->currentUser) {
                return;
            }
    
            $exercise = $event->getObject();
            $visitor = $event->getVisitor();
    
            $visitor->setData(
                'user_results',
                $this->serializer->toArray($exercise->getUserResults($this->currentUser))
            );
        }
    }
    

    【讨论】:

    • 我用正确的事件post_serialize 更新了我的答案,但它不起作用。请以粗体查看我的最后一条评论。
    • 更新了我的答案。 $results 变量不应包含任何对象。请在调用 setData 方法之前对其进行序列化。
    • 问题是getUserResults返回了ExerciseResult的列表。如果我在调用setData 之前对其进行序列化,它不会显示为序列化字符串(编码)吗?
    • 你应该把它序列化成一个数组。您可以将 jms 序列化程序注入此侦听器类,并使用“toArray”方法将ExerciseResult 实体序列化为数组。或者您可以在您的练习结果实体中实现 toArray 方法以返回具有重要属性的数组,这些属性应包含在练习实体序列化中。
    • 您能否将其添加到示例中以便我接受答案?谢谢。
    猜你喜欢
    • 2015-02-13
    • 2019-07-14
    • 2011-10-20
    • 2021-12-04
    • 2019-12-11
    • 2013-06-16
    • 2013-08-04
    • 2018-12-12
    • 2012-05-30
    相关资源
    最近更新 更多