2012-08-07 94 views
15

我正在使用Symfony2,Doctrine,FOSRestBundle和JMSSerializer构建REST API。避免使用Doctrine实体和JMS串行器递归

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

是否有一种简单的方法来限制这种情况,只需要包含foreignIds?

+0

我觉得完全一样的问题已经奔张贴在这里:http://stackoverflow.com/questions/6706485/how-to-encode-doctrine-entities-to -json-in-symfony-2-0-ajax-application。你会发现一些使用JMSSerializer的答案。 – 2012-08-13 10:01:34

回答

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(); 
      } 

它会停止延迟加载你的实体。如果这是您正在寻找的内容,那么请继续前进,并在之前不要延迟加载。

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

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

+0

听起来像它可以为我工作,本周没有机会工作,但会在本周末看看。 – wgcrouch 2012-08-16 08:44:13

+0

几个月前已经知道了这一点。感谢提供这个。 http://stackoverflow.com/questions/11575345/disable-doctrine-2-lazy-loading-when-using-jms-serializer – 2012-10-16 21:49:20

17

使用JMS排除策略。被列入使用上类别的实体,您不希望包括儿童和产品相关实体的注解

例子:

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

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

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

} 

看那JMSSerializer docs以获取更多信息。

编辑:

例如,你可以使用部分关键字来只选择你需要的数据。虽然我不能在我的生活中使用 ,但如果我将实体对象传递给序列化程序(即使在DoctrineProxyHandler中禁用加载时),则禁止加载完整的相关实体(向下两级),但如果我使用数组,比它不使用教条延迟加载虽然代理(如预期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 
      } 
      ] 
     } 
     ] 
    } 
    ] 

} 
] 

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

+3

场所更新:由于[版本0.11](https://github.com/schmittjoh/JMSSerializerBundle/blob/master/UPGRADING.md#upgrading-from-010-to-011)JMS Seralizer芯萃取出来的束,以便而不是'JMS \ SerializerBundle \注释\ ExclusionPolicy'这将是'JMS \串行\注释\ ExclusionPolicy'和'JMS \ SerializerBundle \注释\ Exclude'将是'JMS \串行\注释\ Exclude'。 – 2013-04-26 06:01:42

9

在JMSSerializer的最新版本只是一个更新,你应该看就是

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

,而不是

串行\处理器\ DoctrineProxyHandler

的地方

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

在您的应用程序/ config.yuml补充一点:

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

您可以复制从JMS \串行\此事件\用户\ DoctrineProxySubscriber类到您的\包\事件\ DoctrineProxySubscriber并注释掉$对象 - > __负载();线

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

+1

我喜欢这样的解决方案,但是它不是为我工作。我创建了自己的DoctrineProxySubscriber,并在序列化过程中检查了它,但从未到达注释行,并始终延迟加载相关实体。我不希望序列化程序对数据库进行查询,但另一方面,我希望它序列化已获取的数据。我会很感激任何想法。 – Radzikowski 2014-05-22 17:26:58

+0

我评论了线$对象 - > __负载和东西去** **差不多像我需要它...现在它返回'id_thing:{id_thing:1}'insetead对象..有什么办法返回'id_thing:1'只? – KnF 2014-09-12 05:59:20

+0

啊,另一件事...我没有使用Symfony2 ..只是JMSSerializer和Doctrine ..我如何创建自定义订阅者? – KnF 2014-09-12 06:00:34

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; 
} 
0

这是防止延迟加载一个或多个可用作JMS Serializer ExclusionStrategy的关联的类。

use Doctrine\ORM\PersistentCollection; 
use Doctrine\ORM\Proxy\Proxy; 
use JMS\Serializer\Context; 
use JMS\Serializer\Exclusion\ExclusionStrategyInterface; 
use JMS\Serializer\Metadata\ClassMetadata; 
use JMS\Serializer\Metadata\PropertyMetadata; 
use JMS\Serializer\SerializationContext; 

/** 
* Class OnlyLoadedAssociationsExclusionStrategy 
* 
* http://stackoverflow.com/questions/11851197/avoiding-recursion-with-doctrine-entities-and-jmsserializer 
*/ 
class OnlyLoadedAssociationsExclusionStrategy implements ExclusionStrategyInterface 
{ 
    public function shouldSkipClass(ClassMetadata $metadata, Context $context) 
    { 
    } 

    public function shouldSkipProperty(PropertyMetadata $property, Context $context) 
    { 
     if ($context instanceof SerializationContext){ 
      $vistingSet=$context->getVisitingSet(); 

      //iterate over object to get last object 
      foreach ($vistingSet as $v){ 
       $currentObject=$v; 
      } 

      $propertyValue=$property->getValue($currentObject); 

      if ($propertyValue instanceof Proxy){ 
       // skip not loaded one association 
       if (!$propertyValue->__isInitialized__){ 
        return true; 
       } 
      } 

      if ($propertyValue instanceof PersistentCollection){ 
       // skip not loaded many association 
       if (!$propertyValue->isInitialized()){ 
        return true; 
       } 
      } 
     } 
     return false; 
    } 
} 

用例:

$serializationContext->addExclusionStrategy(
    new OnlyLoadedAssociationsExclusionStrategy() 
); 
+0

也张贴在这里:http://stackoverflow.com/questions/11575345/disable-doctrine-2-lazy-loading-when-using-jms-serializer – 2015-08-24 15:31:04

+0

你应该避免复制和粘贴跨多个问题的答案。当你这样做时,[主持人注意力会自动提高标志](http://meta.stackoverflow.com/questions/270311/can-we-auto-flag-answers-that-are-clearly-copy-pasted/ 270341#270341)。相反,只有一个问题是另一个问题的重复。 – CubeJockey 2015-08-24 15:54:30