Defining your counter caches - ml-archive/nodes-php-counter-cache GitHub Wiki
Before we can define a counter cache, we need to have a relation.
Continuing on the case explained on the previous page, this means we need to implement a user
relation in our Post
model.
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [];
}
}
By having a user
relation, we're now able to create a counter cache between the User
model and the Post
model.
The way you define your relation, is to add it to the array
inside the counterCaches
method. The key
of the array should be the name of the field, you wish to update on the related model.
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [
'posts_count' => 'user'
];
}
}
In our case we want to update the field posts_count
on the User
model, which we have created a relation to through the user
method on our Post
model.
To learn how and when to execute the counter caches, jump to the next page in this Wiki.
← Implementing counter caching on your models | Executing your counter caches →
Advanced counter caches
It's not always sufficient to just use the plain relation. Sometimes you might need add WHERE
conditions or you might even to update the same named fields in two different relations. No worries, this is how you do it.
Updating the "same" field on two different models
Let's use the same example as we have throughout the Wiki. We have a User
model and a Post
model. On the User
model we have a field posts_count
which we want to update every time a Post
has been created.
But what if we also have a Category
model, which also contains a posts_count
field, which we also want to update every time a Post
has been created? Don't worry, there is support for that.
You simply convert the value
of your counter cache into an array
so it contains a group of relations.
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Post has one category
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function category()
{
return $this->hasOne(Category::class, 'post_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [
'posts_count' => ['user', 'category']
];
}
}
Adding conditions to your counter caches
You're also able to add additional conditions to your counter caches. You might only want to update the counter caches for User
who is confirmed.
Adding conditions is a little bit more complex. To avoid having to update the package every time Laravel make changes to Eloquent or add new smart features, the package is using the Builder
which is available on the relation, meaning that all the genius methods like where
, orWhere
, whereNull
etc. is available.
To add conditions to your counter caches, you need to convert the value
of your simple counter to an array
, where the key
of that array is the name of your relation and the value is another array.
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [
'posts_count' => [
'user' => []
]
];
}
}
The array
of the relation user
is where you add your conditions. The key
should be the name of your condition, where the value should be an array of the parameters, that the condition method takes.
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [
'posts_count' => [
'user' => [
'where' => [
['is_confirmed', '=', 1]
]
]
]
];
}
}
The reason to for the nested array inside the array of parameters is because the package is utilizing PHP's call_user_func_array
method.
Let's use Laravel's where
method as an example. The method looks like this:
public function where($column, $operator = null, $value = null, $boolean = 'and')
In counter cache terms this translates to:
'where' => [
[$column, $operator, $value, $boolean]
]
Another example is Laravel's whereNull
. The method looks like this:
public function whereNull($column, $boolean = 'and', $not = false)
In counter cache terms this translates to:
'whereNull' => [
[$column, $boolean, $not]
]
Hopefully you get the picture now 🙂
Using conditions with multiple relations on the same named field
Just to clarify, you of course also add additional conditions, when you have counter caches with the same named field on multiple relations:
use Nodes\CounterCache\CounterCacheable;
class Post implements CounterCacheable
{
/**
* Post belongs to user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* Post has one category
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function category()
{
return $this->hasOne(Category::class, 'post_id', 'id');
}
/**
* Retrieve array of counter caches.
*
* @return array
*/
public function counterCaches()
{
return [
'posts_count' => [
'user' => [
'where' => [
['is_confirmed', '=', 1]
]
],
'category' => [
'whereNull' => [
['is_deleted']
]
]
];
}
}