Laravel eloquent get relation count

PhpLaravel

Php Problem Overview


I use Laravel 5.3.

I have 2 tables :

Articles
---------
id
cat_id
title

And

Category
---------
id
parent_id
title

I have defined my relations in my models :

// Article model
public function category()
{
    return $this->belongsTo(Category::class);
}

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

Is there an easy way using Eloquent to have a list a categories with count of articles. The difficulty is that I want to group categories where id_parent = 0, i.e. I want to display only parent categories with count of articles in children.

I tried something like that :

	$category = new \App\Models\Category();
	$categoryTable = $category->getTable();
	
    return $category->leftJoin('article', 'article.cat_id', '=', 'category.id')
		->whereIn('article.cat_id', function($query)
			{
				$query->select('cat_id')
					->from('categories')
					->where('categories.parent_id', ???)
					->orWhere($this->tableName .'.cat_id', $id);
			})
		->groupBy('cat_id');

But I am lost...

Php Solutions


Solution 1 - Php

you can use withCount(). It is available from 5.3 version

for more info about eloquent visit : https://laravel.com/docs/5.3/eloquent-relationships

Solution 2 - Php

Define a articles() relation in your Category model as:

public function articles() 
{
	return $this->hasMany(Article::class, 'cat_id');
}

Then you can try it as:

Category::where('parent_id', 0)->withCount('articles')->get();

Solution 3 - Php

You can use the hasManyThrough() Eloquent method to fetch all of the childrens' Articles, then add the article counts together in a nice little getter. I added the getter to the $appends array on the model to help illustrate it in the Tinker output.

class Category extends Model
{

	protected $appends = [
		'articleCount'
	];

    public function articles()
    {
    	return $this->hasMany(Article::class);
    }

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

    public function childrenArticles()
    {
    	return $this->hasManyThrough(Article::class, Category::class, 'parent_id');
    }

    public function getArticleCountAttribute()
    {
    	return $this->articles()->count() + $this->childrenArticles()->count();
    }
}

Here's the Tinker output:

Psy Shell v0.8.0 (PHP 7.0.6 — cli) by Justin Hileman
>>> $cat = App\Category::first();
=> App\Category {#677
     id: "1",
     name: "Cooking",
     parent_id: null,
     created_at: "2016-12-15 18:31:57",
     updated_at: "2016-12-15 18:31:57",
   }
>>> $cat->toArray();
=> [
     "id" => 1,
     "name" => "Cooking",
     "parent_id" => null,
     "created_at" => "2016-12-15 18:31:57",
     "updated_at" => "2016-12-15 18:31:57",
     "articleCount" => 79,
   ]
>>> 

If you want to restrict your Category query to ones that have children that have articles, you could do that using the has() method:

Category::has('children.articles')->get();

Here's more on the has() method:

https://laravel.com/docs/5.3/eloquent-relationships#querying-relationship-existence

And the hasManyThrough() method:

https://laravel.com/docs/5.3/eloquent-relationships#has-many-through

Solution 4 - Php

This should work:

$category
->where('categories.parent_id', 0)
->leftJoin('article', 'article.cat_id', '=', 'categories.id')
->select('categories.id', \DB::raw('COUNT(article.id)'))
->groupBy('categories.id')
->get();

The above query will get you category IDs and count of all articles that belong to the category.

After reading your question and comments again, if I understand correctly you want to get the count of all articles that belong to those categories (with parent_id = 0) + the count of articles that belong to sub categories (those with parent_id = (id of some category)).

Now I have no way of testing this easily, but I think something along these lines should work for that.

$category
->where('categories.parent_id', 0)
->leftJoin('article', 'article.cat_id', '=', 'categories.id')
->leftJoin('categories as c2', 'c2.parent_id', '=', 'categories.id')
->leftJoin('article as a2', 'a2.cat_id', '=', 'c2.id')
->select('categories.id', \DB::raw('(COUNT(article.id)) + (COUNT(a2.id)) as count'))
->groupBy('categories.id')
->get();

That beign said, I think you're better of having a column named count in categories and update it each time a new article gets added. For performance.

Solution 5 - Php

I am sure somebody is still going through this, I was able to solve it the following way, suppose I have an Agent model and a Schedule model, i.e. one agent may have many schedules:

class Schedule extends Model {
  public function agent() {
    return $this->belongsTo(Agent::class, 'agent_id');
  }
}

class Agent extends Model {
  public function user(){
    return $this->belongsTo(User::class);
  }

  public function schedules(){
    return $this->hasMany(Schedule::class);
  }
}

Well some agents may not necessarily have schedules assigned, thus, I filtered those before calling the with() method, like this:

$agents = Agents::whereIn(
    'id', 
    Schedule::distinct()->pluck('agent_id')
)->with('schedules')->get();

Hope this helps!.

Solution 6 - Php

  public function NoOfStudent()
    {
        return $this->hasMany(UserAssignment::class,'assignment_id','id');
    }



$assignment = Assignment::select('id','batch_id','title','description','attachment','last_submission_date',DB::raw('(CASE WHEN type = 9 THEN "Quiz Type"  ELSE "Descriptive" END) AS assignment_type'),DB::raw('(CASE WHEN status = 1 THEN "Assigned"  ELSE "Not Assigned" END) AS status'))
	    			  ->with('assignmentBatch:id,batch_number')
	    			  ->where('assignments.instructor_id',auth('api')->user()->id)
                      ->orderBy('created_at','DESC');
        if(!$request->user_id){
            $assignment =$assignment->withCount('NoOfStudent');
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVincent DecauxView Question on Stackoverflow
Solution 1 - Phpkapilpatwa93View Answer on Stackoverflow
Solution 2 - PhpAmit GuptaView Answer on Stackoverflow
Solution 3 - PhpCarter FortView Answer on Stackoverflow
Solution 4 - PhpDevKView Answer on Stackoverflow
Solution 5 - PhpCarlos_E.View Answer on Stackoverflow
Solution 6 - PhpkushView Answer on Stackoverflow