Как я могу ограничить количество вложенных объектов в платформе API? - PullRequest
0 голосов
/ 29 апреля 2020

Имея две связанные сущности, скажем, Автор и Книгу, я могу ограничить (или разбить на страницы) результаты авторов, но не число результатов связанной сущности Книги, которая всегда показывает всю коллекцию.

проблема заключается в том, что у авторов могут быть сотни книг, что приводит к получению JSON огромного и тяжелого для анализа, поэтому я пытаюсь получить, например, только последние 5 книг.

Я уверен, что я что-то упущено, так как я думаю, что это, вероятно, распространенный сценарий, но я не могу найти ничего ни в документах, ни здесь, в StackOverflow.

Я начинаю с Api Platform, любой совет был бы оценен!

1 Ответ

1 голос
/ 30 апреля 2020

Я наконец решил это, создав нормализатор для сущности, но я все еще думаю, что это должно быть более простое решение.

Вот что я должен был сделать, следуя примеру авторов / книг:

Добавьте установщик в сущность Author, чтобы переопределить коллекцию Книг Авторов:

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

}

Создайте нормализатор для сущности Author:

// 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'
...