Skip to content

[9.x] Add ability to define "with" relations as a nested array#42690

Merged
taylorotwell merged 3 commits into
laravel:9.xfrom
timacdonald:nested-withs
Jun 9, 2022
Merged

[9.x] Add ability to define "with" relations as a nested array#42690
taylorotwell merged 3 commits into
laravel:9.xfrom
timacdonald:nested-withs

Conversation

@timacdonald

Copy link
Copy Markdown
Member

Purpose

This PR aims to add the ability to specify relationship "withs" as a PHP array, rather than dot notation.

Usage

// before...

User::with([
    'avatar',
    'posts.tags',
    'posts.author',
    'posts.featureImage',
    'posts.comments.tags' => fn ($q) => $q->latest(),
])->get();

// after...

User::with([
    'avatar',
    'posts' => [
        'tags',
        'author',
        'featureImage',
        'comments' => [
            'tags' => fn ($q) => $q->latest(),
        ],
    ],
])->get();

Advanced

// supports chaining...

User::with([
    'posts' => fn ($q) => $q->withCount('comments')->with([
        'comments' => [
            'tags',
        ],
    ]),
])->get();

// supports specifying attributes inline, although I think with this notation you
// should probably be specifying selects via a constraint.

User::with([
    'posts:id,title,user_id' => [
        'comments:id,content,post_id' => [
            'tags',
        ],
    ],
])->get();

// supports mixing notation. Not saying you should, just saying you can 🙈

User::with([
    'posts' => [
        'comments',
    ],
    'posts.image',
])->get();

// merges constraints from different notation - again, not saying you should. just saying you can.

User::with([
    'posts.comments' => fn ($q) => $q->with('tags'),
    'posts:id,title,user_id' => [
        'comments' => fn ($q) => $q->withCount('tags'),
    ],
])->get();

What isn't supported

I should note that merging of selects from different notation is not supported. I don't see this as a problem, as the current dot notation suffers from the same constraint...

// does not select "id" and "name"
User::query()
    ->with(['users:id,name'])
    ->with(['users:email'])
    ->get();

// nor does this...

User::query()
    ->with(['users' => fn ($q) => $q->select(['id', 'name'])])
    ->with(['users:email'])
    ->get();

and so the following is explicitly not supported...

User::with([
    'posts.comments' => fn ($q) => $q->addSelect(['id']),
    'posts' => [
        'comments' => fn ($q) => $q->addSelect(['post_id'])
    ],
])->get();

User::with([
    'posts' => [
        'comments:post_id,title'
    ],
    'posts.comments:id',
])->get();

or any other mixture of notation with duplicate selects.

Notes

I'd love to get some eyes on this from a range of people who are deep in Eloquent land.

Comment thread src/Illuminate/Database/Eloquent/Builder.php Outdated
@taylorotwell taylorotwell merged commit 1c0b3d2 into laravel:9.x Jun 9, 2022
@timacdonald timacdonald deleted the nested-withs branch June 13, 2022 02:31
@OzanKurt

Copy link
Copy Markdown
Contributor

Does this also work on ->load()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants