Skip to content

Including relations ​

allowedIncludes() lists the relations a request may load. The include parameter takes a list of relation names:

php
QueryBuilder::for(User::class)
    ->allowedIncludes('posts', 'permissions')
    ->get();
GET /users?include=posts,permissions

The relations are eager loaded with with(). An include not in the list throws InvalidIncludeQuery.

Nested relations ​

php
->allowedIncludes('posts.comments')

A nested path allows every prefix too: include=posts and include=posts.comments both work.

Another relation name ​

php
use MyProgs\QueryBuilder\AllowedInclude;

AllowedInclude::relationship('profile', 'userProfile')

The client includes profile, the query loads userProfile.

Aggregates ​

A relation name in allowedIncludes() also allows its count and existence:

GET /users?include=postsCount,postsExists

They are loaded with withCount() and withExists(), so every user gets posts_count and posts_exists. Nested paths allow the aggregates of their first relation only. The suffixes come from the suffixes config key.

To allow an aggregate without the relation itself, list it on its own:

php
->allowedIncludes('postsCount', AllowedInclude::exists('postsExists'))

Other aggregates take the relation and the column:

php
AllowedInclude::min('postsViewsMin', 'posts', 'views'),
AllowedInclude::max('postsViewsMax', 'posts', 'views'),
AllowedInclude::sum('postsViewsSum', 'posts', 'views'),
AllowedInclude::avg('postsViewsAvg', 'posts', 'views'),

They are loaded with withMin(), withMax(), withSum() and withAvg(), so the attribute is posts_min_views and so on. Every aggregate takes an optional closure that constrains the related query:

php
AllowedInclude::count('publishedPostsCount', 'posts', fn ($query) => $query->where('published', true))

Callback includes ​

php
AllowedInclude::callback('posts', fn ($query) => $query->where('published', true))

The closure constrains the eager load of the relation: it is passed to with() as ['posts' => $closure]. The third argument names the relation when it differs from the include name.

Custom include classes ​

Implement MyProgs\QueryBuilder\Includes\IncludeInterface and register it with custom():

php
use Illuminate\Database\Eloquent\Builder;
use MyProgs\QueryBuilder\Includes\IncludeInterface;

class AggregateInclude implements IncludeInterface
{
    public function __construct(protected string $column, protected string $function) {}

    public function __invoke(Builder $query, string $relations): void
    {
        $query->withAggregate($relations, $this->column, $this->function);
    }
}
php
AllowedInclude::custom('comments_sum_votes', new AggregateInclude('votes', 'sum'), 'comments')

The class receives the root query and the relation name, the third argument.

Fields of included relations ​

With allowedFields(), the client may narrow the columns of an included relation, and the keys the relation needs are added automatically. See Selecting fields.