Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

cosmin_paunica's avatar

Eloquent: with() not working for relation depending on a model attribute

Hello!

In my app, I have a User model and a License model. A user can have multiple licenses. Also, a user can be a parent or a subuser. This is denoted by the parent_id field in the users table, which is NULL if the user is a parent. Only the parent user has licenses.

In the User model, I have a method for retrieving the latest license:

public function license(): HasOne
{
    return !is_null($this->parent_id) ? $this->parent->license() : $this->hasOne(License::class)->latestOfMany();
}

If the user is a subuser, it returns the parent's license. Otherwise, it returns a HasOne relationship.

This works fine when doing something like this:

$subuser = User::find($subuserId);
dd($subuser->license);

However, if I try to use with(), for example:

$subuser = User::with('license')->find($subuserId);
dd($subuser->license);

this will fail, because inside the license() method, the model's attributes are not loaded at the time with() runs (i.e. $this->attributes is null).

How can I make this work?

0 likes
9 replies
tykus's avatar

You should not (cannot) rely on instance state inside relationship methods

1 like
cosmin_paunica's avatar

@tykus Thank you! I will avoid doing that.

Is there another way I can modify User::license() so that it can fetch the user's license if it is a parent user, or the parent's license if it is a subuser? Maybe with a custom query?

cosmin_paunica's avatar

@tykus I know that I can pass an array containing closures every time I call with(), but I would like to be able to simply call User::with('license')->find($id).

tykus's avatar

@cosmin_paunica I would need to give that some thought... you basically want a query to return the latestOfMany License records identified by either the subuser's parent ID or the the user's own ID???

1 like
cosmin_paunica's avatar

@tykus What I want is that license() knows by itself if it should retrieve the user's license if the user is a parent, or the parent's license if the user is a subuser, so that I can then call User::with('license').

I suppose writing a query as the one you mentioned is not difficult using the Query Builder. My problem is how can I use that query inside license() to retrieve the correct license?

Edit: the latestOfMany() part isn't really relevant here. Also thanks for your help so far

cosmin_paunica's avatar

Something that crossed my mind is that maybe there's a way I could set the local key by which the relationship works, something like this:

public function license(): HasOne
{
    $localKey = ... // this will be either 'id' or 'parent_id', depending on whether the user is a parent or a subuser
    return $this->hasOne(License::class, 'user_id', $localKey)->latestOfMany();
}

As an experiment, I hardcoded $localKey = 'parent_id' and it worked as expected when I called license() on a subuser.

However I don't know if there's any way of setting $localKey without accessing the instance state.

Snapey's avatar
Snapey
Best Answer
Level 122

you should get the collection with the child licence (if there is one) and parent licence, and then create an accessor on the model so that when you get model->licence, the accessor can do the evaluation against the model instance

You will need to choose a different name for the accessor so that it does not conflict with the license relation.

1 like
cosmin_paunica's avatar

@Snapey I made it work starting from your solution!

I made the license() method simply return the user's license, without checking if it is a subuser:

public function license(): HasOne
{
    return $this->hasOne(License::class)->latestOfMany();
}

Then I added a method named getTeamLicenseAttribute(), which looks like this:

public function getTeamLicenseAttribute()
{
    return !is_null($this->parent_id) ? $this->parent->teamLicense : $this->license;
}

Now if I acces an user with User::with('license', 'parent.license') it works as expected.

Thank you for your help!

Please or to participate in this conversation.