symfony - 如何以编程方式更改 Doctrine 实体属性的序列化

标签 symfony jmsserializerbundle symfony-2.8 jms-serializer

我有一个由 JMSSerializer 正确序列化/反序列化的 Dashboard 实体(通过 JMSSerializerBundle):

/**
 * @ORM\Table(name="dashboard", schema="myappID")
 * @ORM\Entity(repositoryClass="Belka\MyBundle\Entity\Repository\DashboardRepository")
 */
class Dashboard
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue("SEQUENCE")
     *
     * @Serializer\Groups({"o-all", "o-all-getCDashboard", "i-p2-editDashboard"})
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Belka\MyBundle\Entity\User")
     *
     * @ORM\JoinTable(name="users_dashboards_associated",
     *      schema="myAppID",
     *      joinColumns={@ORM\JoinColumn(name="dashboard_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
     *      )
     *
     * @Serializer\groups({
     *     "o-p2-create",
     *     "i-p2-create",
     *     "o-p2-patch",
     *     "i-p2-editDashboard"
     * })
     */
    protected $users;
}

并且我使用 JMSSerializer 的 jms_serializer.doctrine_object_constructor 作为对象构造函数。 一切都像魅力一样,但我有以下极端情况:有时我必须将 Dashboard::$users 设置为字符串(即当客户端发送语义不正确的 users > 属性,检查后我返回该对象以及一个字符串以通知它。这对于前端应用程序来说非常方便)。 JMSSerializer takes advantage of the Doctrine's annotation ,但在这种情况下,我真的想以编程方式覆盖它,因为这是一个非常极端的情况。我想到的有两种方法:

  1. 有没有办法将 SerializationContext 设置为将 Dashboard::$users 映射为字符串属性?
  2. 有没有办法在序列化之前更改 Doctrine 的元数据?
  3. 还有其他我没有意识到的选项?

欢迎任何建议

最佳答案

我找到了一个解决方案,尽管它没有考虑嵌套实体的属性(has-a 关系)。这意味着要访问整个图表,但我没有时间研究优秀的 JMSSSerializer 的内部结构。不过,它非常适合强制第一级实体的属性:

首先,需要一个预序列化订阅者。它将循环访问 protected 属性并检查它们是否包含字符串。是这样,序列化的类型将被覆盖。

class SerializationSubscriber implements EventSubscriberInterface
{

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

    public function onPreSerialize(PreSerializeEvent $event)
    {
        $entity = $event->getObject();
        $metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($event->getType()['name']);
        $reflect = new \ReflectionClass($entity);
        $props = $reflect->getProperties(\ReflectionProperty::IS_PROTECTED);

        foreach ($props as $prop) {
            $prop->setAccessible(true);

            if (is_string($prop->getValue($entity))) {
                // here is the magic
                $metadata->propertyMetadata[$prop->name]->type = array('name' => 'string', 'params' => array());
            }
        }
    }
}

接下来,我不想每次序列化某些内容时都听这个。这是我的一项服务中的一个极端案例。我们可以利用 JMS\Serializer\EventDispatcher\EventDispatcher::addSubscriber,尽管 EventDispatcher 服务被声明为私有(private)。 因此,让我们通过编译器 channel 将该服务转变为public,以便利用addSubscriber:

class MyBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(new OverrideJmsSerializerEventDispatcherDefPass());
    }
}

...让我们将该服务转变为公共(public)服务

class OverrideJmsSerializerEventDispatcherDefPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $definition = $container->getDefinition('jms_serializer.event_dispatcher');
        $definition->setPublic(true);
    }
}

因此,我们可以将其注入(inject)到我们的服务中。 IE。在我的 services.yml 中:

  belka.mybundle.dashboardhandler:
      class: Belka\MyBundle\Handlers\DashboardHandler
      calls:
          - [setEventDispatcher, ["@jms_serializer.event_dispatcher"]]

好吧,现在我们可以在需要时轻松添加订阅者,而无需在每次应用程序执行序列化时增加另一个监听器的负担:

$serializationSubscriber = new SerializationSubscriber();
$this->eventDispatcher->addSubscriber($serializationSubscriber);

请随意使用访问整个实体图的解决方案来完成答案。那太好了。

关于symfony - 如何以编程方式更改 Doctrine 实体属性的序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43472400/

相关文章:

symfony - FOSRestBundle 和 JMSSerializer 运行时公开

mongodb - 我正在尝试使用 JMSSerizial Bundle 序列化嵌入式 mongodb 文档

php - Symfony 2.8 -> 3.4 升级 IsGranted ('IS_AUTHENTICATED_ANONYMOUSLY' ) 引发错误

php - 将约束传递给 symfony 集合类型

compiler-errors - 如何找出为什么无法加载由Symfony v2.8创建的网页?

php - 使用\PHPUnit_Framework_TestCase 或 ..\TestCase

symfony - 使用事件监听器创建 preUpdate 或 preFlush 事件

php - 将 symfony2 NotFoundHttpException 移动到不同的文件

symfony - 仅适用于 PROD 环境的自定义错误页面?

symfony - 在 Symfony2 中运行 PHPUnit 测试时,JMS Serializer 不读取配置