【问题标题】:How can i limit the number of nested entities in API Platform?如何限制 API 平台中嵌套实体的数量?
【发布时间】:2020-04-29 19:03:44
【问题描述】:

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

问题是作者可能有数百本书,导致生成的 JSON 巨大且难以解析,因此我试图仅获取最后 5 本书。

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    向 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'
    

    【讨论】:

    • 也许你应该看看Pagination - API Platform,硬编码不是答案,你打算如何获取其余书籍或相关作者?
    • 你完全正确。我发布了这个答案,因为这样的一段代码在我写这个问题时会很有帮助,但绝对只是一个开始的地方。
    • 几周前我刚刚发现了 Api 平台,我真的认为它非常强大且可定制,但在处理关系/子资源时,IMO 事情开始变得艰难。还认为在这件事上缺乏文档,而不是平台本身对子资源结果定制的限制。为什么我不能只复制粘贴文档中的所有内容? :P
    • 你说的很对,几周以来我一直在做一个复杂的项目,为了把事情弄清楚Extending API Platform
    • 非常感谢您分享您的经验。
    猜你喜欢
    • 2022-06-27
    • 1970-01-01
    • 2021-06-19
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多