2

I have a complex Model with multiple defined relations. In this example I would want to count the Like model and create a property named likes so it can be returned from a REST service.

Is it possible to eager load a model count into a dynamic property?

$beat = Post::with(
         array(
            'user',
            'likes' => function($q){
                $q->count();
            }
        ))
        ->where('id', $id)
        ->first();
Ben Elgar
  • 715
  • 10
  • 23
Niko Kovačič
  • 187
  • 2
  • 12

2 Answers2

4

Assuming you are having Post->hasMany->Like relationship and you have declared likes relationship as:

class Post{

  public function likes(){
   return $this->hasMany('Like');
  }
}

create a new function say likeCountRelation as:

public function likeCountRelation()
{
    $a = $this->likes();

    return $a->selectRaw($a->getForeignKey() . ', count(*) as count')->groupBy($a->getForeignKey());
}

now you can override __get() function as:

public function __get($attribute)
{

    if (array_key_exists($attribute, $this->attributes)) {
        return $this->attributes[$attribute];
    }

    switch ($attribute) {

        case 'likesCount':
            return $this->attributes[$attribute] = $this->likesCountRelation->first() ? $this->likesCountRelation->first()->count : 0;
            break;

        default:
            return parent::__get($attribute);

    }
}

or you can use getattribute function as :

public function getLikesCountAttribute(){
 return $this->likesCountRelation->first() ? $this->likesCountRelation->first()->count : 0;
}

and simply access likesCount as $post->likesCount you can even eager load it like:

$posts=Post::with('likesCountRelation')->get();
 foreach($post as $post){
  $post->likesCount;
 }

NOTE: Same logic can be used for morph many relationships.

Manish
  • 1,946
  • 2
  • 24
  • 36
1

You should use the SQL Group By statement in order to make it works. You can rewrite your query like the following one.

$beat = Post::with(
         array(
            'user',
            'likes' => function($q) {
                // The post_id foreign key is needed, 
                // so Eloquent could rearrange the relationship between them
                $q->select( array(DB::raw("count(*) as like_count"), "post_id") )
                  ->groupBy("post_id")
            }
        ))
        ->where('id', $id)
        ->first();

The result of likes is a Collection object with one element. I'm assuming the relationship between model Post and Like is Post hasMany Like. So you can access the count like this.

$beat->likes->first()->like_count;

I'm not tested code above but it should works.

rioastamal
  • 75
  • 6
  • The code you provided doesn't work. The like_count is not defined – edi9999 Mar 13 '14 at 11:33
  • @edi9999, It should be defined by Laravel on-the-fly since it was populated as column. Take a look at: DB::raw("count(*) as like_count") – rioastamal Mar 16 '14 at 15:57
  • @edi9999 I'm trying to do something similar, but it wasn't working when the count was 0 (since `first()` returns null). In order to deal with that special case, I did this: `$beat->likes->first() ? $beat->likes->first()->like_count : 0`. Maybe you had the same problem? – Daniel James May 01 '14 at 14:38