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

Moritz's avatar

Model->with infinite loop problem

Hello, i have a database with multiple hasMany relations, like 'category' has many 'objects', 'objects' has many 'comments', etc. When i now get a list of objects, i want to include the category and the number of comments. (do this for all Models, so comments should include object and category should include object_count)

Include the category i can do with protected $with = array('category'); in my object model. The comment_count i do like this: (original source: https://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/)

class Object extends Model
{
    protected $with = array('commentsInfo');

    protected $hidden = array('commentsInfo');

    protected $appends = array('comment_count');

    public function category()
    {
        return $this->belongsTo('App\Models\Category');
    }

    public function comments()
    {
        return $this->hasMany('App\Models\Comment');
    }

    public function commentsInfo()
    {
        return $this->hasOne('App\Models\Comment')
                    ->selectRaw('object_id, count(*) as comment_count')
                    ->groupBy('object_id');
    }

    public function getCommentCountAttribute()
    {
        if (!$this->relationLoaded('commentsInfo')) {
            $this->load('commentsInfo');
        }

        $related = $this->getRelation('commentsInfo');

        return ($related) ? (int) $related->comment_count : 0;
    }
}

Both works good, but together it ends in an infinite loop. So when Eloquent tries to count the comments, it loads the Comment Model which includes Objects again, but the Object again tries to load the comment_count, and so on. So far i understand what the Problem with this design is.

So is there a way to get the count relation as model property without loading the relations of the counted model?

Regards Moritz

0 likes
4 replies
Moritz's avatar

Thanks, that could help. But i have some special cases left where this does not work.

I can use a where subquery inside withCount, but as far as i can see withCount can't rename the result column. This is a problem when i use the same table twice.

Category::withCount([
        'objects' => function ($query) {
            $query->where('type', '=', '1');
        },
        'objects' => function ($query) {
            $query->where('type', '=', '2');
        }
    ])->get();

Or is there a way to rename the withCount result?

Is there a way to include the withCount directly in the Model? This would be very nice to have. So if i include the category within the object Object::with('categrory'), the category should still have the object_count.

(And in one case i need the average, would be nice if there is some withAvg() method, but in this one case i think the old code above would do it without the loop problem.)

Regards Moritz

ohffs's avatar

I think you'd have to code that up as a helper on the model yourself - maybe make it a trait so you can re-use it though.

Moritz's avatar

withCount() can now rename the result. I made an pull request, which was accepted: https://github.com/laravel/framework/pull/15279

And i think i could live with the "withCount() result" not directly included in the Model. Should be ok if just my repository adds this. Then the result is like this: (models as json)

Category:

{
    "name":"cat1",
    "object_count":1
}

Object:

{
    "name":"obj1",
    "category": {
        "name":"cat1"
        // no object_count property here
    }
}

So you first answer "use withCount()" is now a good solution. Thanks.

Please or to participate in this conversation.