ismail_bourbie's avatar

Laravel self-reference Relationship and load() method

I have the following code:

public function othersFromSameLab(): HasMany
{
    return $this->hasMany(self::class, 'laboratory_id', 'laboratory_id')
                ->whereNot('id', $this->id);
}

When I use the load method, e.g., $myModel->load('othersFromSameLab'), the whereNot constraint does not work because the id is null. However, accessing the relationship directly using $myModel->othersFromSameLab works as expected.

Is there a solution or a better approach to resolve this?

0 likes
10 replies
Merklin's avatar

To use load, you should first get the model and then run load. like:

$user = User::all() or $user = User::where(some condition) and THEN use $user->load('relation') to load the relation.

On the contrary, using $user->where('some_condition')->with('relation') directly loads the relation.

Using load() gives you the ability to load only the main model, and decide later if the relation has to be loaded. Like, when using load(), you have 2 queries that are executed separately, while when using with() those 2 queries are executed simultaneously and the relation is accessible on the go.

Snapey's avatar

@merklin

neither of your examples is correct

$user = User::all() returns a collection.

$user = User::where(some condition) returns a query builder

->load() must only be used on a hydrated instance of the model in order for the id to be set. The OP does not explain what $myModel contains so we can't say if the example should or should not work.

@ismail_bourbie the best solution is to ensure that laboratory_id is null on its own record, and then you don't need the additional where condition which breaks things like ->with()

1 like
ismail_bourbie's avatar

@Snapey $myModel is just a model with 2 relations

public function laboratory(): BelongsTo
{
    return $this->belongsTo(Laboratory::class);
}

public function othersFromSameLab(): HasMany
{
    return $this->hasMany(self::class, 'laboratory_id', 'laboratory_id')
                ->whereNot('id', $this->id);
}
Snapey's avatar

You can modify your relationship like

    return $this->hasMany(self::class, 'laboratory_id', 'laboratory_id')
			   ->whereColumn('id', '!=' , 'laboratory_id');
ismail_bourbie's avatar

@snapey

public function laboratory(): BelongsTo
{
    return $this->belongsTo(Laboratory::class);
}

public function othersFromSameLab(): HasMany
{
    return $this->hasMany(self::class, 'laboratory_id', 'laboratory_id')
                ->whereNot('id', $this->id);
}
tykus's avatar

@ismail_bourbie this relationship definition must be incorrect; the local key and foreign should be different for a self referencing relationship. Assuming the local key is id, then:

return $this->hasMany(self::class, 'laboratory_id')
			   ->whereColumn('id', '!=' , 'laboratory_id');
ismail_bourbie's avatar

@tykus Local key should belaboratory_id (NOT id) - because we're matching based on having the same laboratory_id.

tykus's avatar

@ismail_bourbie I doubt. Just take a moment to think about the query that will result from this HasMany relationship.

Please or to participate in this conversation.