Как сериализовать фрагмент ArrayCollection с помощью JMS Serializer?

Я хочу сериализовать в объект JSON Category с набором объектов Presentation (см. ниже) для использования в REST API.

Конечная точка будет выглядеть примерно так /api/v1/categories/1

Когда набор данных мал и когда Category имеет только 5-10 связанных Presentations, результирующий ответ не слишком велик. Однако, когда Category начинает иметь, скажем, 100 или 200 связанных Presentations, тогда, очевидно, я не хочу возвращать их все, но хотел бы «разбить» результаты на страницы, например. при вызове конечной точки:

/api/v1/categories/1?page=2 - вернет только "2-я страница"

/api/v1/categories/1/page=3 - вернет "3-я страница"

или даже это может быть с offset и limit:

/api/v1/categories/1?offset=20&limit=10

но проблема в следующем: как заставить сериализатор JMS сериализовать только часть коллекции?

/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
 */
class Category
{

    /**
     * @var string
     * @ORM\Column(type="string")
     * @JMS\Expose()
     * @JMS\Groups({"get-category"})
     */
    private $title;


    // ...

    /**
     * @var ArrayCollection
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Presentation", mappedBy="categories", fetch="EXTRA_LAZY")
     * @JMS\Groups({"get-category"})
     * @JMS\Expose()
     */
    private $presentations;


    // ...

}

пс. Я знаю, что, например, если я хочу всегда получать первые 5 элементов коллекции, я могу добавить созданные @VirtualProperty и нарезать доктрину ArrayCollection, как показано ниже. Но проблема здесь в том, что я не могу передать параметры смещения этому методу. Поскольку JMSSerializer будет вызывать его внутри где-то...

/**
 * @JMS\VirtualProperty()
 *
 */
public function getFirstFivePresentations(){
    return $this->presentations->slice(0,5);
}

person Dimitry K    schedule 03.02.2016    source источник
comment
Не было бы более разумно ограничить результаты сбора в вашем запросе, чем всегда загружать 100% из них и пытаться нарезать их постфактум?   -  person Jason Roman    schedule 03.02.2016
comment
@JasonRoman ваша проблема верна, если стратегия извлечения ассоциации - LAZY или EAGER. Однако в моем случае я пометил ассоциацию как EXTRA_LAZY doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/ @ORM\ManyToMany(targetEntity="Presentation", mappedBy="categories", fetch="EXTRA_LAZY") означает, что коллекция не будет загружена полностью. И все запросы SELECT откладываются до момента, когда я вызываю slice(0,5), что приводит к SELECT xxx FROM presentation OFFSET 0 LIMIT 5.   -  person Dimitry K    schedule 03.02.2016
comment
Не могли бы вы просто написать собственный запрос, чтобы получить то, что вам нужно?   -  person Jason Roman    schedule 03.02.2016
comment
@JasonRoman Я думаю, что начинаю понимать твою точку зрения... позволь мне попробовать...   -  person Dimitry K    schedule 03.02.2016
comment
Да, я бы не стал просто полагаться на ассоциации здесь, особенно для API, и особенно в случае, когда гидратация может действительно замедлить поиск в любом случае.   -  person Jason Roman    schedule 03.02.2016
comment
@JasonRoman, как бы вы предложили построить запрос? Я попробовал доктрину Paginator gist.github.com/dimkir/e79b2a878c8e0d6aec29, но она ограничивает только количество Categories, а не количество Category->Presentation объектов...   -  person Dimitry K    schedule 03.02.2016
comment
Давайте продолжим обсуждение в чате.   -  person Dimitry K    schedule 03.02.2016


Ответы (1)


Вы пытаетесь реализовать неправильный подход в своем REST API. У каждой сущности должен быть свой путь.

Правильный способ - иметь две разные конечные точки:

/api/v1/categories/1 -> Серийная категория без презентаций

/api/v1/categories/1/presentations -> Серийный сборник презентаций

И здесь вы должны использовать пагинацию

/api/v1/categories/1/presentations?offset=20&limit=10

person Jekis    schedule 22.02.2016
comment
Я согласен с вами в том, что включение presentations в ресурс categories не кажется хрестоматийным решением. Однако из приложения, которое я использую, у меня есть представление, которое показывает /Список категорий с несколькими примерами презентаций/, и я не хочу делать несколько запросов API из приложения для получения этих данных. И я вижу больше вариантов использования для подобных сценариев — для большинства представлений приложения требуется сочетание данных из разных ресурсов.... - person Dimitry K; 03.03.2016
comment
Создайте свойство $presentationPreviews в Category и не сопоставляйте его с аннотациями доктрины, создайте для него сеттер. Исключить $presentations из сериализации. В контроллере, после того, как категории были выбраны, зациклите их и извлеките их презентации, используя класс репозитория (установите критерии и ограничения, которые вам нравятся). Установить полученную коллекцию презентаций с помощью $category->setPresentationPreviews($fetchedPresentations). Настройте сериализацию для сериализации свойства $presentationPreviews. Вот и все! - person Jekis; 04.03.2016