Laravel API Resources supercharged to get rid of N+1 problems.
netsells/laravel-resourceful is a Laravel package for laravel api resources supercharged to get rid of n+1 problems..
It currently has 2 GitHub stars and 44.401 downloads on Packagist (latest version v2.0.0).
Install it with composer require netsells/laravel-resourceful.
Discover more Laravel packages by netsells
or browse all Laravel packages to compare alternatives.
Last updated
Package that helps you use Laravel API Resources without killing your database! Say goodbye to N+1 :wave:
Picture a User model that has an "avatar" relation to a File model:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->avatar->url,
];
}
}
To return a list of users from our API, we could have such a controller action:
namespace App\Http\Controllers;
use App\Models\User;
class UserController extends \Illuminate\Routing\Controller
{
public function index()
{
return UserResource::collection(User::paginate());
}
}
In the example above:
We have a classic N+1 problem. The usual solution in Laravel is to perform eager loading within the controller:
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Routing\Controller;
class UserController extends Controller
{
public function index()
{
return UserResource::collection(User::query()->with('avatar')->paginate());
}
}
This works, but has few problems:
This package fixes all those problems and makes the N+1 problem within your API a thing of the past!
using composer:
composer require netsells/laravel-resourceful
This package is a drop in replacement for the official API Resources. Instead of extending the builtin Illuminate\Http\Resources\Json\JsonResource we extend Netsells\Http\Resources\Json\JsonResource.
Continuing with the example above, we get:
namespace App\Http\Resources;
use Netsells\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->avatar->url,
];
}
}
Of course, this doesn't change anything and things still work as they did with N+1 problem, however this first step allows you to easily switch over to this package.
There are few ways now to supercharge your API resource.
First way is to select the relations for eager loading in a dedicated preloads method.
You should return one or more "deferred resources", which can be easily created by calling preload helper with a list of relations you want loaded.
The preloads method can optionally accept a Laravel Request as its first argument. Within the preloads method we have access to the Eloquent model being resolved.
This allows us to introduce conditional logic for preloading specific relations based on request parameters / model attributes.
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Netsells\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function preloads()
{
return $this->preload('avatar');
}
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->avatar->url,
];
}
}
Now before the toArray is called, the avatar relation will be loaded for all the users in collection, so we've got full access to $this->avatar without issuing further queries.
More examples can be seen in the tests.
Second way is to provide a callback which will be called with the resolved relations.
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Netsells\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->use('avatar', fn ($avatar) => $avatar->url),
];
}
}
Now when toArray is called, the avatar relation hasn't been loaded yet. Our code in the toArray method will execute for all the users in collection, and each call to the use method will queue a relation to be loaded. We'll then resolve all the queued relations for all users in the most optimal way, and invoke the callback function with resolved relations.
More examples can be seen in the tests.
Third way is to use an inline shortcut which is very convenient for loading one or many nested API Resources.
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Netsells\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar' => $this->one('avatar', AvatarResource::class),
];
}
}
class AvatarResource extends JsonResource
{
public function preloads()
{
return $this->preload('file');
}
public function toArray(): array
{
return [
'id' => $this->id,
'width' => $this->width,
'height' => $this->height,
'url' => $this->file->s3Url(),
];
}
}
In the example above, we have introduced a nested resource, where a UserResource embeds a separate AvatarResource. Each resource is responsible for eager loading its own relations, and together all relations are loaded in most optimal way.