3

I want to serialize into JSON entity Category with collection of Presentation entities (see below) to use for REST API.

The endpoint will look something like this /api/v1/categories/1

When dataset is small and when Category only has only 5-10 related Presentations then the resulting response is not too large. However when Category starts to have let's say 100 or 200 related Presentations then obviously I do not want to return all of them, but would like to "paginate" the results, eg. when calling endpoint:

/api/v1/categories/1?page=2 - would return only "2nd page"

/api/v1/categories/1/page=3 - would return "3rd page"

or even it can be with offset and limit:

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

but the problem is: how to make JMS serializer serialize only a slice of the collection?

/**
 * @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;


    // ...

}

ps. I know that for example if I want to get always first 5 elements of the collection, I can add created @VirtualProperty and slice the doctrine ArrayCollection as shown below. But the problem here is that I cannot pass the offset parameters to this method. As it would be called internally by JMSSerializer somewhere...

/**
 * @JMS\VirtualProperty()
 *
 */
public function getFirstFivePresentations(){
    return $this->presentations->slice(0,5);
}
Dimitry K
  • 2,236
  • 1
  • 28
  • 37
  • Wouldn't it make more sense to limit the collection results in your query, rather than always loading 100% of them and trying to slice them after the fact? – Jason Roman Feb 03 '16 at 15:09
  • @JasonRoman your concern is correct in case association's fetching strategy is `LAZY` or `EAGER`. However in my case I have marked the association as `EXTRA_LAZY` http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html `@ORM\ManyToMany(targetEntity="Presentation", mappedBy="categories", fetch="EXTRA_LAZY")` which means that collection wouldn't be loaded in full. And all SELECT queries are deferred till the moment I call `slice(0,5)` which results in `SELECT xxx FROM presentation OFFSET 0 LIMIT 5`. – Dimitry K Feb 03 '16 at 15:20
  • Couldn't you just write a custom query though to retrieve what you need? – Jason Roman Feb 03 '16 at 15:46
  • @JasonRoman I think I am starting to see your point... let me try.. – Dimitry K Feb 03 '16 at 15:54
  • Yeah I wouldn't simply rely on the associations here, especially for an API, and especially in a case where hydration might really slow down the retrieval anyway – Jason Roman Feb 03 '16 at 16:17
  • @JasonRoman how would you suggest to build the query? I tried doctrine `Paginator` https://gist.github.com/dimkir/e79b2a878c8e0d6aec29 but it only limits the quantity of `Categories`, not quantity of `Category->Presentation` entities... – Dimitry K Feb 03 '16 at 17:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/102504/discussion-between-dimitry-k-and-jason-roman). – Dimitry K Feb 03 '16 at 17:29

1 Answers1

1

You are trying to implement the incorrect approach in your REST API. Each entity must have it's own path.

The right way is to have two different endpoints:

/api/v1/categories/1 -> Serialized category with no presentations

/api/v1/categories/1/presentations -> Serialized collection of presentaions

And here you should use pagination

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

Jekis
  • 4,274
  • 2
  • 36
  • 42
  • I agree with you that including `presentations` within the `categories` resource doesn't seem to be textbook solution. However from the application I use I have a view which shows /List of categories with few sample presentations/ and I do not want to make multiple API requests from the app to retrieve this data. And I see more use cases for similar scenarios - most of the views of the app require a mix of data from different resources.... – Dimitry K Mar 03 '16 at 11:21
  • Create property `$presentationPreviews` in `Category` and don't map it with doctrine annotations, create setter for it. Exclude `$presentations` from being serialized. In controller, after categores were fetched, loop them and fetch their presentations using repository class (set criteria and limits you like). Set fetched collection of presentations using `$category->setPresentationPreviews($fetchedPresentations)`. Configure serialization to serialize `$presentationPreviews` property. And that's it! – Jekis Mar 04 '16 at 08:45