Kenneth_H's avatar

Eager Loading parent categories

Hi So I am building a small scale ecommerce application where I want to eager load the parent category if it exists. I am already eager loading metadata as this is needed everywhere where I use the category and I am using the $with protected field on the model for that. However, I am facing an issue when I try to do this with a category that is a root category, which means that it has no parent.

protected $with = ['metadata', 'parent'];

public function parent()
{
        return $this->belongsTo(Category::class, 'parent_id');
}
public function children()
{
        return $this->hasMany(Category::class, 'parent_id');
}

Everything works fine, when all categories either have a parent og no parent at all. As soon as I mix them, the request takes some time to run, even with only two or three categories created. Once the request fails, php artisan servehas cancelled it self. I have checked storage/logs/laravel.log and nothing is there.

Is there a way for me to always eager load the parent category, but have the relationship return null, like a OneToMany relationship?

0 likes
8 replies
aurawindsurfing's avatar

Correct me if I'm wrong but should it not be like that?

public function parent()
{
        return $this->hasMany(Category::class, 'parent_id');
}
public function children()
{
        return $this->belongsTo(Category::class, 'parent_id');
}

Besides you are creating model relationship between one and the same model? Maybe that is possible but it just looks odd to me.

Kenneth_H's avatar

No, not really. The relationship is that a category can have one parent, but multiple children. So the relationship seems correct to me. From what I have read online, the way I have done it seems to be correct when you want to create a parent-child relationship in the same table.

Do you have another idea for how to accomplish this?

Snapey's avatar

don't use $with on the model or you end up with this sort of issue (a recursive query that runs until there is no memory left)

It only takes parent_id to point to itself, or two records to point to each other to get this situation

just check your data, are all the parent_ids valid or null? (no zeros in there)

Kenneth_H's avatar

I have reset the database and created some dummy categories and still the same behaviour. My migration allows for null on the parent_id, so no default value should be set.

Schema::create('categories', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->unsignedInteger('parent_id')->nullable();
            $table->text('body')->nullable();
            $table->timestamps();
        });

Perhaps it would be better to remove the usage of $with, but then it becomes a little more troublesome when I actually want to get a specific category and all children and their children.

Kenneth_H's avatar

I have come up with somewhat of a temporary solution. I have removed protected $with = ['children']; from the model and then just use with('children') on the query builder instance on the model. This also works when I add parent into the mix, as it will then just return null if there is no parent_id assigned to the record. It is just strange that this does not work when using $with on the model. However, when I was eager loading this using $with on the model, I got everything recursively from the requested category instance. Fx. when I want to render a category list in a view, I want to be able to get this list. If I use with() from the query builder, I only get the records that are direct children of the given category. I really wanted to at least have the functionality to get the children eager loaded fx. 3 levels deep. Is there an option/method for that or would I have to write such code myself?

Snapey's avatar

Think it through..

parent eager loads child which eager loads parent which eager loads child which eager loads parent etc etc

children loading children is fine

1 like
Kenneth_H's avatar

I get your point. But how would I solve the specific use case, where I actually want to recursively eager load all children and their children?

I know that I would need to use some kind of caching to improve performance.

Snapey's avatar
Snapey
Best Answer
Level 122

that's fine. Use $with to eager load children or to eager load parent. Just don't do it for both.

Please or to participate in this conversation.