首页 > 解决方案 > 如何限制 API 平台中嵌套实体的数量?

问题描述

有两个相关的实体,比如说作者和书籍,我可以限制(或分页)作者的结果,但不能限制其相关实体书籍的结果数量,它总是显示整个集合。

问题是作者可能有数百本书使生成的 JSON 变得庞大而沉重,因此我试图仅获取最后 5 本书。

我确定我遗漏了一些东西,因为我认为这可能是一种常见的情况,但我在文档和 StackOverflow 中都找不到任何东西。

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

标签: symfonydoctrine-ormsymfony4api-platform.com

解决方案


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

这是我必须做的,按照作者/书籍的例子:

向 Author 实体添加一个 setter 以覆盖 Author's Book 集合:

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

最后手动将规范器注册为服务(使用 autowire 导致我遇到循环引用问题):

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

推荐阅读