Как мога да огранича броя на вложените обекти в API Platform?

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

Проблемът е, че авторите може да имат стотици книги, което прави получения JSON огромен и тежък за анализ, така че се опитвам да получа, например, само последните 5 книги.

Сигурен съм, че пропускам нещо, тъй като мисля, че това вероятно е често срещан сценарий, но не мога да намеря нищо в документите, нито тук в StackOverflow.

Започвам с Api Platform, всеки намек ще бъде оценен!


person campsjos    schedule 29.04.2020    source източник


Отговори (1)


Най-накрая го реших, създавайки нормализатор за обекта, но все още мисля, че трябва да е по-просто решение.

Ето какво трябваше да направя, следвайки примера за автори / книги:

Добавете настройка към обекта Author, за да замените колекцията 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'
person campsjos    schedule 30.04.2020
comment
Може би трябва да погледнете Пагинация - API платформа, твърдото кодиране не е отговор , как ще вземеш останалите книги или сродни автори? - person Maulik Parmar; 02.05.2020
comment
Вие сте напълно прав. Публикувах този отговор, защото част от код като този би бил полезен, когато написах въпроса, но определено това е само място, от което да започна. - person campsjos; 02.05.2020
comment
Току-що открих Api Platform преди няколко седмици и наистина смятам, че е много мощна и персонализирана, но IMO нещата започват да стават трудни, когато работим върху взаимоотношения / подресурси. Освен това помислете повече за липсата на документация по този въпрос, отколкото за това, че самата платформа ограничава персонализирането на резултатите от подресурсите. Защо не мога просто да копирам и поставя всичко от документите? :P - person campsjos; 02.05.2020
comment
Вие сте напълно прав, работя върху сложен проект от няколко седмици, за да изясня нещата Разширяване на API платформата ‹--- Тази страница е най-важната, за да започнете. В крайна сметка избрах DTO, MessageHandlers с нормализатор / денормализатор, вместо да използвам персонализирани операции. Добавя сложност при прикачването като 6 логически клас към ресурс, но в края на деня ще помогне много при разделянето на логиката. Ако трябва да бъда честен, кривата на учене тук е толкова стръмна, но когато започнете да разбирате вътрешностите, става по-лесно. - person Maulik Parmar; 04.05.2020
comment
Благодаря ви много, че споделихте опита си. - person campsjos; 04.05.2020