How to solve the n+1 problem introduced by fractal
The popular package, thephpleague/fractal aims to protect your API consumers from output inconsistencies and also provide an elegant way to include relationships for complex data structures.
for 100 records per page == 400 queries!
The problem with the includes is that all relations are being lazily loaded.
TL;DR use Laravel 5.5+ Eloquent Resources and Spatie’s query builder
getContent() getters were created manually inside the
Review model, in order to decouple database schema from code.
For this example, the includes may look like:
Besides the nested data key, the additional database queries are killing the performance.
There are some workarounds, for example, we could use eagerly loaded relations, but the question is “how many?”. What happens when the front-end requires more includes? Do we eager load everything, even nested relations?
Lazy eager load means more than one request and, moreover, you have to implement it manually, to override the
callIncludeMethod , after fractal has checked that a given include is valid.
Joins are too expensive, especially for large tables.
The solution I found consists of this couple:
Laravel 5.5 introduced Eloquent Resources, which basically are a sort of native transformers on steroids: https://laravel.com/docs/5.6/eloquent-resources#conditional-relationships
Spatie’s query builder https://github.com/spatie/laravel-query-builder whose ?include=product,product.merchant enables you to get rid of the fractal package.
Why on steroids? The conditional relationship enables the application to eagerly load JUST the requested relationships, namely
product.merchant only when they are requested.
Let’s suppose the consumer does not require the
product.merchant nested relation
merchant relation is not loaded and the key is missing entirely from the generated output! How cool is that?
Returns status code 400, with the message: Given include(s) `not-whitelisted.nested-relation` are not allowed. Allowed include(s) are `product, product.merchant`
Another benefit of Laravel’s Resource is that you can structure the output better. No more nested
data keys on every included relation
Spatie’s query builder can also filter, sort and restrict the nested includes (which fractal can’t do). Moreover, the
orWhere statement won’t fool the application to display all protected resources.