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

时间:2012-08-07 17:37:18

标签: php symfony doctrine-orm fosuserbundle

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

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

是否有一种简单的方法来限制它,只是包含foreignIds?

4 个答案:

答案 0 :(得分:19)

使用JMS排除政策。

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

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

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

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

}

查看JMSSerializer docs了解更多信息。

修改

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

使用示例实体的示例:

$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
            }
            ]
        }
        ]
    }
    ]

}
]

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

答案 1 :(得分:9)

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

JMS \串行\此事件\订户\ DoctrineProxySubscriber

而不是

串行\处理程序\ DoctrineProxyHandler

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

在你的app / config.yuml中添加:

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

您可以将类从JMS \ Serializer \ EventDispatcher \ Subscriber \ DoctrineProxySubscriber复制到您的\ 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

答案 2 :(得分:8)

检查JMSSerializerBundle上的Serializer/Handler/DoctrineProxyHandler.php文件。现在,如果你评论这一行:

public function serialize(VisitorInterface $visitor, $data, $type, &$handled)
    {
        if (($data instanceof Proxy || $data instanceof ORMProxy) && (!$data->__isInitialized__ || get_class($data) === $type)) {
            $handled = true;

            if (!$data->__isInitialized__) {
                //$data->__load();
            }

它将停止延迟加载您的实体。如果这是您正在寻找的,那么请继续create your own handler,不要在其中进行延迟加载。

如果这不正确,我建议您根据自己的喜好自定义实体,然后再将它们发送到JMSSerializerBundle。例如,在我想要ID的任何相关实体中,而在其他实体中,我需要一个自定义列名,如代码,名称或任何东西。

我只是创建我的实体对象的副本,然后开始获取关系所需的字段。然后,我序列化该副本。 JMSSerializerBundle不会延迟加载,因为我已经提供了正确的字段。

答案 3 :(得分:0)

这是一个在不使用连接的情况下以通用方式选择一对一或一对多关联实体的ID的函数。

function selectWithAssociations($doctrine, $className) {

    $em = $doctrine->getManager();
    $meta = $em->getClassMetadata($className);

    //explicitly get IDs of associated entities
    $assocClauses = array();
    foreach ($meta->getAssociationMappings() as $assocName => $assoc) {
        if (isset($assoc['joinTable'])) {
            //todo: doesn't handle many to many associations
        } else {
            $assocClauses[] = ", IDENTITY(e.$assocName) AS $assocName";
        }
    }

    //run custom DQL query
    $q = $em->createQuery('SELECT e AS _d' . implode('', $assocClauses) . ' FROM ' . $className . ' e');
    $result = $q->getArrayResult();

    return $result;
}