Связи (includes)
includes() перечисляет, что принимает include=:
public function includes(): array
{
return [
'profile',
'posts.comments',
Relationship::to('author', UserQuery::class),
Relationship::count('posts'),
Relationship::max('posts', 'views'),
Relationship::callback('recentPosts', fn ($query) => $query->where('created_at', '>', now()->subWeek()), 'posts'),
Relationship::morphTo('commentable', [Post::class => PostQuery::class, Video::class => VideoQuery::class]),
];
}Связи
Путь связи строкой ('posts.comments') разрешает эту связь; каждый префикс пути тоже разрешён.
Вложенные определения
Relationship::to('posts', PostQuery::class) описывает связь её собственным определением: под этим путём разрешены его поля, подгрузки связей и агрегаты.
Определения могут ссылаться друг на друга: UserQuery подгружает posts, а PostQuery — author, описанный UserQuery. Определение, которое повторяется на своём же пути, там становится листом, поэтому include=posts.author разрешён с полями UserQuery, а posts.author.posts — нет.
->withFilters() публикует фильтры вложенного определения на корне (см. Фильтры), а ->withSorts() — его сортировки (см. Сортировки).
Агрегаты
Relationship::count('posts'), exists, min, max, sum и avg. Они запрашиваются как postsCount, postsExists, postsMaxViews; суффиксы берутся из конфига query-builder.suffixes, а ->as('name') меняет имя. Каждый принимает необязательное замыкание, которое ограничивает запрос к связанной модели.
Связи с замыканием
Relationship::callback('recentPosts', fn ($query) => ..., 'posts') загружает связь с условием под собственным именем.
morphTo
Relationship::morphTo('commentable', [Post::class => PostQuery::class, Video::class => VideoQuery::class]) описывает morphTo с определением для каждой связанной модели. fields[posts] сужает посты под commentable, а вложенные подгрузки связей типа загружаются только для моделей этого типа.