避免使用 Doctrine 实体和 JMSserializer 进行递归

2022-08-30 23:05:32

我正在使用Symfony2,Doctrine,FOSRestBundle和JMSSerializer构建一个REST API。

我遇到的问题是,在序列化我的实体时,序列化程序会拉入任何相关的实体。例如,对于作为板一部分的故事的一部分的任务,因此在序列化任务时,我获得的输出包括包含板的故事,然后包括板上的所有其他故事。

有没有一种简单的方法来限制这一点,而只是包括外国Ids?


答案 1

使用 JMS 排除策略。

在类别实体上使用批注的示例,其中您不希望包括要包含的子实体和产品相关实体:

use ...
    JMS\SerializerBundle\Annotation\ExclusionPolicy,
    JMS\SerializerBundle\Annotation\Exclude,
    ...;

/**
 * ...
 * @ExclusionPolicy("none")
 */
class Category
{
   /**
    * ...
    * @Exclude
    */
   private $children;

   /**
    * ...
    * @Exclude
    */
   private $products;

}

请查看 JMSSerializer 文档以获取更多信息。

编辑:

例如,您可以使用部分关键字仅选择所需的数据。虽然我不能,在我的一生中,如果我将实体对象传递给序列化程序(即使在TherismProxyHandler中禁用加载时),禁用完整相关实体的加载(向下两级),但是如果我使用数组,那么它不会使用通过代理的 doctrine 延迟加载(正如预期的ofc)。

使用示例实体的示例:

$dql = "SELECT t, s, partial b.{id}, partial ss.{id}
        FROM Acme\AppBundle\Entity\Task t
        JOIN t.story s
        JOIN s.board b
        JOIN b.stories ss"

$q = $this->_em-createQuery($dql);

$result = $q->getArrayResult();

这样你会得到这样的东西:

[
{
    id: 33,
    title: "My Task",
    story: [
    {
        id: 554,
        board: [
        {
            id: 14,
            stories: [
            {
                id: 554
            },
            {
                id: 3424
            },
            {
                id: 3487
            }
            ]
        }
        ]
    }
    ]

}
]

附言:我实际上对这个“问题”很感兴趣。无论如何,我将看到如何提出如何在不使用数组结果的情况下序列化实体对象的解决方案。


答案 2

只是最新版本的JMSSerializer中的更新,您应该查看的地方是

JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber

而不是

Serializer\Handler\DoctrineProxyHandler

要覆盖默认的延迟加载行为,应该定义自己的事件订阅者。

在您的 app/config.yuml 中添加以下内容:

parameters:
    ...
    jms_serializer.doctrine_proxy_subscriber.class: Your\Bundle\Event\DoctrineProxySubscriber

您可以将类从 JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber 复制到 Your\Bundle\Event\DoctrineProxySubscriber 并注释掉$object->__load();线

public function onPreSerialize(PreSerializeEvent $event)
{
    $object = $event->getObject();
    $type = $event->getType();

    // If the set type name is not an actual class, but a faked type for which a custom handler exists, we do not
    // modify it with this subscriber. Also, we forgo autoloading here as an instance of this type is already created,
    // so it must be loaded if its a real class.
    $virtualType = ! class_exists($type['name'], false);

    if ($object instanceof PersistentCollection) {
        if ( ! $virtualType) {
            $event->setType('ArrayCollection');
        }

        return;
    }

    if ( ! $object instanceof Proxy && ! $object instanceof ORMProxy) {
        return;
    }

     //$object->__load(); Just comment this out

    if ( ! $virtualType) {
        $event->setType(get_parent_class($object));
    }
}

更新:我最终编写了自己的简化版本的序列化工具:https://github.com/dlin-me/array-converter-bundle


推荐