Php 序列化程序Symfony上的回调

Php 序列化程序Symfony上的回调,php,symfony,serialization,Php,Symfony,Serialization,我正在运行symfony2.7,并尝试将一个对象(条令实体)输出为JSON 当我规范化对象时,我想转换它的一些值。为此,我在中找到了“setCallbacks”方法,但我有点困惑于如何将其应用到我的案例中 在调用Symfonys serializer服务时,是否有方法调用规范化器上设置的“setCallbacks”方法 下面是一个简短的例子,说明我正在努力实现的目标: //ExampleController.php public function getJSONOrderByIdAction(

我正在运行symfony2.7,并尝试将一个对象(条令实体)输出为JSON

当我规范化对象时,我想转换它的一些值。为此,我在中找到了“setCallbacks”方法,但我有点困惑于如何将其应用到我的案例中

在调用Symfonys serializer服务时,是否有方法调用规范化器上设置的“setCallbacks”方法

下面是一个简短的例子,说明我正在努力实现的目标:

//ExampleController.php

public function getJSONOrderByIdAction($id) {
    $serializer = $this->get('serializer');
    $normalizer = $serializer->getNormalizer(); // <- This is what I'm unable to do

    $dateTimeToString = function ($dateTime) {
        return $dateTime instanceof \DateTime ? $dateTime->format(\DateTime::ISO8601) : '';
    };

    $normalizer->setCallbacks(['time' => $dateTimeToString]);


    $order = $this->getDoctrine()->find("AppBundle:Order", $id);

    return new JsonResponse(["order" => $serializer->normalize($order, null, ["groups" => ["public"]])]);
}
//ExampleController.php
公共函数getJSONOrderByIdAction($id){
$serializer=$this->get('serializer');

$normalizer=$serializer->getNormalizer();//在我看来,您似乎试图让事情变得过于复杂。以下是我在需要将实体序列化为JSON时采取的方法:

PHP2.5及以上版本允许您在对象上实现该方法,只需直接调用对象即可

如果您仍在使用PHP2.4,只需手动调用对象上的
jsonSerialize()

例如:

/**
 * @ORM\Entity
 */
class MyEntity {
    ...
    public function jsonSerialize() {
        $data = array("foo" => $this->bar());
        // add other data here ...
        return $data
    }
}
然后在调用代码中:

// for PHP 2.5 and up:
$normalized = json_encode($myEntityInstance);

// for PHP 2.4 and below
$normalized = json_encode($myEntityInstance->jsonSerialize());

默认序列化程序服务是在依赖项注入阶段创建的,序列化程序接口不允许编辑(完全)检索规范化程序

我认为你(至少)有三个选择:

  • 将自定义规范化程序添加到默认序列化程序服务
  • 将NormalizableInterface添加到实体中
  • 按照您的尝试创建一个新的序列化程序服务(或文档建议的本地对象)
  • 我认为在您的场景中,案例1是首选的(因为案例2很快就会变得无聊)

    我将这样做;首先创建一个自定义规范化器

    <?php
    namespace AppBundle; 
    
    class DateTimeNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
    {
        /**
         * {@inheritdoc}
         */
        public function normalize($object, $format = null, array $context = array())
        {
            return $object->format(\DateTime::ISO8601);
        }
    
        /**
         * {@inheritdoc}
         */
        public function denormalize($data, $class, $format = null, array $context = array())
        {
            return new $class($data);
        }
    
        /**
         * Checks if the given class is a DateTime.
         *
         * @param mixed  $data   Data to normalize.
         * @param string $format The format being (de-)serialized from or into.
         *
         * @return bool
         */
        public function supportsNormalization($data, $format = null)
        {
            return $data instanceof \DateTime;
        }
    
        /**
         * Checks if the given class is a DateTime.
         *
         * @param mixed  $data   Data to denormalize from.
         * @param string $type   The class to which the data should be denormalized.
         * @param string $format The format being deserialized from.
         *
         * @return bool
         */
        public function supportsDenormalization($data, $type, $format = null)
        {
            $class = new \ReflectionClass($type);
    
            return $class->isSubclassOf('\DateTime');
        }
    }
    

    我自己的解决方案

    按照giosh94mhz的建议,我尝试切换到JMS序列化程序,但最终返回到Symfonys序列化程序


    JMS序列化程序介绍了它自己的问题,在为我偶然发现的问题寻找答案时,我发现了一个非常出色的工具,它解释了如何在Symfony中使用实现您自己的规范化程序。

    您可以使用K.Dunglas组件的回调规范化程序

    您可以在ObjectNormalizer(在normalize方法中)中看到这一点

    这意味着您必须在回调数组键中使用不希望规范化的属性的名称

    例如,在我的实体中,我有一个名为“name”的字段,类型为“pgarray”(类似于postgresql的数组)。我不想规范化此数据。相反,我想要一个字符串

    /**
         * $object represent the property "name" because callback is attached to name property (setCallback)
         */
        $nameCallback = function ($object, $outerObject = null) {
            return $object[0];      
        };
        $this->normalizer->setCallbacks(['name' => $dateCallback]);
    

    请记住,由于Symfony 4.2,您必须在DI中使用$context才能使用回调。

    我更愿意让实体尽可能模块化。当前控制器操作必须以某种方式转换对象,这在其他情况下是不需要的。哦,我明白了。是的,您不希望用非标准序列化来混乱实体类ions。我还没有测试过代码,但应该可以。你自己试试,如果需要的话给我一些反馈;我会为社区更新答案。这看起来可能是一个不错的选择。DateTime示例正是他们在Symfonys文档中使用的示例-但假设我的订单有一系列产品,我需要对它们做些什么这也是。我是要创建一个ProductNormalizer,还是需要以某种方式处理集合?另外,我也不完全确定如何将新的规范化器应用于Symfony服务?我认为仅仅创建服务是不够的,序列化器似乎没有“addNormalizer”方法。如果其中一些方法是给定的,我很抱歉。这对我来说是一个全新的领域。@Daniel默认的规范化过程最好是提供一个默认的实现,但默认值通常会转化为丑陋的。没有“addNormalizer”方法(出于某些很好的原因),但标记“serializer.normalizer”是供您使用的。您可以有一个处理多个类的通用
    MyTweeksToNormalizer
    类,但这可能会变得很复杂。或者,您可以对所有实体实现
    NormalizableInterface
    。作为更好的解决方案,您可以切换到
    jms serializer
    以及配置驱动序列化的好处。将NormalizableInterface应用于所有实体不是一个选项,因为我需要能够以不同的方式对同一对象进行规范化。我开始认为JMS序列化程序确实是一种可行的方法。感谢您提供的详细答案。@Daniel an“in object”总是快速简单,但灵活性较低。JMS序列化程序具有“序列化上下文”。通过使用不同的上下文,您可以在不同的情况下选择序列化的方式和内容;所有这些都归结为编写
    $serizalizer->serialize($object,'my context')
    。无论如何,不客气!只要记住+1或接受它就行了;)
    if (isset($this->callbacks[$attribute])) {
        $attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue);
     }
    
    /**
         * $object represent the property "name" because callback is attached to name property (setCallback)
         */
        $nameCallback = function ($object, $outerObject = null) {
            return $object[0];      
        };
        $this->normalizer->setCallbacks(['name' => $dateCallback]);