symfony - JMS 序列化程序 - 交叉引用问题

标签 symfony annotations jmsserializerbundle

我的实体有两个自引用 OneToMany 关系 childrenrevisions

<?php

namespace App\Entity\CMS;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Entity
 * @JMS\ExclusionPolicy("ALL")
 */
class Page
{
    /**
     * @var int
     *
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @var Page[]|ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="CoreBundle\Entity\CMS\Page", mappedBy="parent")
     *
     * @JMS\Groups({"page_children"})
     * @JMS\Expose()
     */
    protected $children;

    /**
     * @var Page[]|ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="CoreBundle\Entity\CMS\Page", mappedBy="page")
     *
     * @JMS\Groups({"revisions"})
     * @JMS\Expose()
     *
     */
    protected $revisions;

    /**
     * @var string
     *
     * @ORM\Column(type="string", value={"main", "revision"})
     *
     * @JMS\Expose()
     *
     */
    protected $type;

    #...
}

我公开了两个集合 - childrenrevisions。此外,type 已公开 - 它指示 Page 是否属于 revisions

请求 {{host}}/api/pages?expand=page_children 返回包含两种类型的 Pages 的结果。

{
    "id": "1",
    "type": "main",
    "children": [
        {
            "id": "3",
            "type": "main",
            "children": [
                {
                    "id": "5",
                    "type": "main",
                    "children": []

                },
                {
                    "id": "6",
                    "type": "revision",
                    "children": []

                }
            ],
        },
        {
            "id": "4,
            "type": "revision",
            "children": []
        }
    ],
    "id": "2',
    "type": "revision",
    "children": []
}

我想从响应 Pages 中排除类型为 revision 的内容。所以我的最终结果是这样的:

{
    "id": "1",
    "type": "main",
    "children": [
        {
            "id": "3",
            "type": "main",
            "children": [
                {
                    "id": "5",
                    "type": "main",
                    "children": []
                }
            ]
        }
    ]
}

通常,我使用 LexikFormFilterBundle 来过滤结果. 但是在这种情况下,组合请求如下:

{{host}}/api/expand=page_children&page_filter[type]=main

仅适用于第一级结果。

我想到了Dynamic Exclusion StrategySubscribing Handler .不幸的是,我无法找出解决方案。

最佳答案

我通过执行 Subscribing Handler 解决了这个问题

<?php

namespace CoreBundle\Serializer\Subscriber\CMS;

use App\Entity\CMS\Page;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use JMS\Serializer\Handler\SubscribingHandlerInterface;

class PageSubscriber extends SubscribingHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            [
                'event' => Events::PRE_SERIALIZE,
                'method' => 'onPreSerialize',
                'class' => Page::class,
                'format' => 'json',
            ],
        ];
    }

    /**
     * @param PreSerializeEvent $event
     */
    public function onPreSerialize(PreSerializeEvent $event)
    {
        $entity = $event->getObject();

        if (!$entity instanceof Page) {
            return;
        }
        if ($this->isSerialisingForGroup($event, 'exclude_revisions')) {
            $this->excludeRevisions($entity);
        }
    }

    /**
     * @param Page $page
     */
    private function excludeRevisions(Page $page): void
    {
        foreach ($page->getChildren() as $child) {
            if ($child->getStatus() === 'revision') {
                $page->removeChild($child);
            }
        }
    }
}

缺点:此时获取所有数据。元素 "type": "revision" 将包含在非第一级中,并且会扩展,这可能导致 Allowed memory size exhausted

另一种可能的方法是使用 Doctrine postLoad 事件。

<?php

namespace App\Service\Doctrine;

use App\Entity\CMS\Page;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use RuntimeException;

class PageListener implements EventSubscriber
{
    /** @var bool $canFlush */
    private $canFlush = true;

    /**
     * {@inheritdoc}
     */
    public function getSubscribedEvents()
    {
        return [
            Events::postLoad,
            Events::onFlush,
        ];
    }

    /**
     * @param Page $page
     */
    public function onPostLoad(Page $page): void
    {
        $children = $page->getChildren();

        foreach ($children as $child) {
            if ($child->getStatus === 'revision') {
                $page->removeChild($child);
            }
        }
    }

    /**
     * @param OnFlushEventArgs $eventArgs
     */
    public function onFlush(OnFlushEventArgs $eventArgs)
    {
        $em = $eventArgs->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityDeletions() as $entityDeletion) {
            if ($entityDeletion instanceof Page){
                throw new RuntimeException('Flushing Page at this point will remove all Revisions.');
            }
        }
    }
}

缺点:可能很危险,并且会缩小 future 可能发生的变化。

关于symfony - JMS 序列化程序 - 交叉引用问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48753490/

相关文章:

mysql - 使用 JPA 注释设置 Long 值的列长度

Symfony 2.7 + JMS 序列化器 + MaxDepth

symfony - 如何使用 JMSSerializerBundle 创建良好的超媒体格式?

Symfony TwigBundle 不加载任何自定义扩展

php - 不能使用 Symfony\Component\HttpFoundation\File\UploadedFile 类型的对象作为数组 Laravel 4.2 文件上传

php - Mandrill SMTP 超时

php - Encoder->IsPasswordValid 必须实现接口(interface)\UserInterface

java - 在Java中组合注解

java - Jackson 将对象字段误认为是 JsonTypeInfo.Id.NAME

json - 使用关联序列化实体