如何限制API平台中嵌套实体的数量?

时间:2020-04-29 19:03:44

标签: symfony doctrine-orm symfony4 api-platform.com

有两个相关的实体,比如Author和Book,我可以限制(或分页)Authors的结果,但不能限制始终显示整个馆藏的与之相关的实体Books的数量。

问题在于Authors可能有数百本书,使得生成的JSON变得庞大而难以解析,因此我试图仅获取例如最后5本书。

我确定我会丢失某些东西,因为我认为这可能是常见的情况,但是我在文档中以及在StackOverflow中都找不到任何内容。

我从Api平台开始,任何提示将不胜感激!

1 个答案:

答案 0 :(得分:1)

我终于解决了它,为实体创建了规范化器,但我仍然认为它必须是一个更简单的解决方案。

在“作者/图书”示例之后,这就是我要做的事情:

向“作者”实体添加一个设置器以覆盖“作者的书”集合:

// src/Entity/Author.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
// ...

/**
 * @ApiResource
 * @ORM\Entity(repositoryClass="App\Repository\AuthorRepository")
 */
class Author
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Book", mappedBy="author", orphanRemoval=true)
     */
    private $books;

    public function __construct()
    {
        $this->books = new ArrayCollection();
    }

    // Getters and setters
    //...

    public function setBooks($books): self
    {
        $this->books = $books;

        return $this;
    }

}

为作者的实体创建规范化器:

// App/Serializer/Normalizer/AuthorNormalizer.php

<?php

namespace App\Serializer\Normalizer;

use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerAwareTrait;

class AuthorNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
{
    use SerializerAwareTrait;

    private $normalizer;

    public function __construct(
        NormalizerInterface $normalizer,
        IriConverterInterface $iriConverter
    ) {
        if (!$normalizer instanceof DenormalizerInterface) {
            throw new \InvalidArgumentException('The normalizer must implement the DenormalizerInterface');
        }
        if (!$normalizer instanceof AbstractItemNormalizer) {
            throw new \InvalidArgumentException('The normalizer must be an instance of AbstractItemNormalizer');
        }
        $handler = function ($entity) use ($iriConverter) {
            return $iriConverter->getIriFromItem($entity);
        };
        $normalizer->setMaxDepthHandler($handler);
        $normalizer->setCircularReferenceHandler($handler);
        $this->normalizer = $normalizer;
    }

    public function denormalize($data, $class, $format = null, array $context = [])
    {
        return $this->normalizer->denormalize($data, $class, $format, $context);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return $this->normalizer->supportsDenormalization($data, $type, $format);
    }

    public function normalize($object, $format = null, array $context = [])
    {
        // Number of desired Books to list
        $limit = 2;
        $newBooksCollection = new ArrayCollection();
        $books = $object->getBooks();
        $booksCount = count($books);
        if ($booksCount > $limit) {

            // Reverse iterate the original Book collection as I just want the last ones
            for ($i = $booksCount; $i > $booksCount - $limit; $i--) {
                $newBooksCollection->add($books->get($i - 1));
            }
        }

        // Setter previously added to the Author entity to override its related Books
        $object->setBooks($newBooksCollection);
        $data = $this->normalizer->normalize($object, $format, $context);

        return $data;
    }

    public function supportsNormalization($data, $format = null)
    {
        return $data instanceof \App\Entity\Author;
    }
}

最后手动将标准化器注册为服务(使用自动装配使我遇到了循环参考问题):

services:
    App\Serializer\Normalizer\AuthorNormalizer:
        autowire: false
        autoconfigure: true
        arguments:
            $normalizer: '@api_platform.jsonld.normalizer.item'
            $iriConverter: '@ApiPlatform\Core\Api\IriConverterInterface'
相关问题