Skip to content

[12.x] Support nested relations on relationLoaded method #55471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 21, 2025

Conversation

tmsperera
Copy link
Contributor

@tmsperera tmsperera commented Apr 18, 2025

Added support to check nested relations when using relationLoaded() method of Eloquent Model

Why?

Current relationLoaded() method only checks single level relation of the Model. Now users can check nested relations.

$user->load('posts.comments');

// Previously
$user->relationLoaded('posts'); // true
$user->relationLoaded('posts.comments'); // false

// Now
$user->relationLoaded('posts'); // true
$user->relationLoaded('posts.comments'); // true

Benefits

End users can check whether the nested relation loaded once and load the desired relation.

It will not break existing features

Technically this new enhancement should not break any existing usage of the method because the code is designed to support single level relation check as well.

@tmsperera tmsperera changed the title Support nested relations on relationLoaded method [12.x] Support nested relations on relationLoaded method Apr 18, 2025
@@ -1070,9 +1070,26 @@ public function getRelation($relation)
* @param string $key
* @return bool
*/
public function relationLoaded($key)
public function relationLoaded(string $key): bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding type hinting to a method's argument, and adding a return type to a method, are breaking changes to anyone overriding this method in a subclass.

As all models extend the Model class, there is a non-zero chance some project might be overriding this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rodrigopedra Yes having return type causes issues. Modified the code.

@rodrigopedra
Copy link
Contributor

I would keep the current check on top of the method body to avoid any performance impact.

Something like this:

public function relationLoaded($key)
{
    if (array_key_exists($key, $this->relations)) {
        return true;
    }

    // augmented logic
}

@tmsperera tmsperera marked this pull request as draft April 19, 2025 06:44
@tmsperera tmsperera marked this pull request as ready for review April 19, 2025 07:05
@tmsperera tmsperera requested a review from rodrigopedra April 20, 2025 20:30
Copy link
Contributor

@rodrigopedra rodrigopedra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't approve it, but looks good to me

@taylorotwell taylorotwell merged commit b6a2af2 into laravel:12.x Apr 21, 2025
58 checks passed
@danharrin
Copy link
Contributor

Hmm this does seem to have caused a problem for me, here's an open source test failure if you are interested: https://github.com/filamentphp/filament/actions/runs/14589554182/job/40966543141

@danharrin
Copy link
Contributor

The issue is that the relationLoaded() method is not just called by users, it's also caused by the framework if you attempt to access an attribute that doesn't exist:

return $this->isRelation($key) || $this->relationLoaded($key)

In this case, the issue is when attempting to access an attribute with a . in its name.

@rodrigopedra
Copy link
Contributor

@tmsperera you might want to add an instanceof while iterating $this->$relation items here:

if ($nestedRelation !== null) {
foreach ($this->$relation as $related) {
if (! $related->relationLoaded($nestedRelation)) {
return false;
}
}
}

Something like this:

if ($nestedRelation !== null) {
    foreach ($this->$relation as $related) {
        if (! $related instanceof Model) {
            return false;
        }

        if (! $related->relationLoaded($nestedRelation)) {
            return false;
        }
    }
}

@danharrin
Copy link
Contributor

Issue opened with reproduction repository: #55518

@tmsperera
Copy link
Contributor Author

@danharrin Great you found the bug. @rodrigopedra great job on the fix. I will add more test cases to avoid such unexpected bugs happens in future as well.

@AhmedAlaa4611
Copy link
Contributor

Reverted in: dc5b445

@tmsperera tmsperera deleted the relation-loaded branch April 24, 2025 17:54
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