The include
query parameter will load any Eloquent relation or relation count on the resulting models.
All includes must be explicitly allowed using allowedIncludes()
. This method takes an array of relationship names or AllowedInclude
instances.
##Basic usage
$users = QueryBuilder::for(User::class)
->allowedIncludes(['posts'])
->get();
You can load multiple relationships by separating them with a comma:
$users = QueryBuilder::for(User::class)
->allowedIncludes(['posts', 'permissions'])
->get();
##Default includes
There is no way to include relationships by default in this package. Default relationships are built-in to Laravel itself using the with()
method on a query:
$users = QueryBuilder::for(User::class)
->allowedIncludes(['friends'])
->with('posts')
->withCount('posts')
->withExists('posts')
->get();
##Disallowed includes
When trying to include relationships that have not been allowed using allowedIncludes()
an InvalidIncludeQuery
exception will be thrown. Its exception message contains the allowed includes for reference.
##Nested relationships
You can load nested relationships using the dot .
notation:
$users = QueryBuilder::for(User::class)
->allowedIncludes(['posts.comments', 'permissions'])
->get();
##Including related model count
Every allowed include will automatically allow requesting its related model count using a Count
suffix. On top of that it's also possible to specifically allow requesting and querying the related model count (and not include the entire relationship).
Under the hood this uses Laravel's withCount method
. Read more about the withCount
method here.
$users = QueryBuilder::for(User::class)
->allowedIncludes([
'posts',
AllowedInclude::count('friendsCount'),
]);
##Including related model exists
Every allowed include will automatically allow requesting its related model exists using a Exists
suffix. On top of that it's also possible to specifically allow requesting and querying the related model exists (and not include the entire relationship).
Under the hood this uses Laravel's withExists method
. Read more about the withExists
method here.
$users = QueryBuilder::for(User::class)
->allowedIncludes([
'posts',
AllowedInclude::exists('friendsExists'),
]);
##Include aliases
It can be useful to specify an alias for an include to enable friendly relationship names. For example, your users table might have a userProfile
relationship, which might be neater just specified as profile
. Using aliases you can specify a new, shorter name for this include:
use Spatie\QueryBuilder\AllowedInclude;
$users = QueryBuilder::for(User::class)
->allowedIncludes(AllowedInclude::relationship('profile', 'userProfile'))
->get();
##Custom includes
You can specify custom includes using the AllowedInclude::custom()
method. Custom includes are instances of invokable classes that implement the \Spatie\QueryBuilder\Includes\IncludeInterface
interface. The __invoke
method will receive the current query builder instance and the include name. This way you can build any query your heart desires.
For example:
use Spatie\QueryBuilder\Includes\IncludeInterface;
use Illuminate\Database\Eloquent\Builder;
use App\Models\Post;
class AggregateInclude implements IncludeInterface
{
protected string $column;
protected string $function;
public function __construct(string $column, string $function)
{
$this->column = $column;
$this->function = $function;
}
public function __invoke(Builder $query, string $relations)
{
$query->withAggregate($relations, $this->column, $this->function);
}
}
$posts = QueryBuilder::for(Post::class)
->allowedIncludes([
AllowedInclude::custom('comments_sum_votes', new AggregateInclude('votes', 'sum'), 'comments'),
])
->get();
##Callback includes
If you want to define a tiny custom include, you can use a callback include. Using AllowedInclude::callback(string $name, Closure $callback, ?string $internalName = null)
you can specify a Closure that will be executed when the includes is requested.
You can modify the Builder
object to add your own query constraints.
For example:
QueryBuilder::for(User::class)
->allowedIncludes([
AllowedInclude::callback('latest_post', function (Builder $query) {
$query->latestOfMany();
}),
]);
##Selecting included fields
You can select only some fields to be included using the allowedFields
method on the query builder.
⚠️ allowedFields
must be called before allowedIncludes
. Otherwise the query builder wont know what fields to include for the requested includes and an exception will be thrown.
##Include casing
Relation/include names will be passed from request URL to the query directly. This means /users?include=blog-posts
will try to load blog-posts
relationship and /users?include=blogPosts
will try to load the blogPosts()
relationship.
##Eloquent API resources
Once the relationships are included, we'd recommend including them in your response by using Eloquent API resources and conditional relationships.