Aggregating Related Models
Counting Related Models
Sometimes you may want to count the number of related models for a given relationship without actually loading the models. To accomplish this, you may use the withCount method. The withCount method will place a {relation}_count attribute on the resulting models:
const posts = await Post.createQuery().withCount('comments').get();
for(const post of posts) {
console.log(post.GetAttribute('comments_count'));
}By passing an array to the withCount method, you may add the "counts" for multiple relations as well as add additional constraints to the queries:
const posts = await Post.createQuery()
.withCount('votes', {
'comments': function (query) {
query.where('content', 'like', 'code%');
}
})
.get();
console.log($posts[0].GetAttribute('votes_count'));
console.log($posts[0].GetAttribute(comments_count));You may also alias the relationship count result, allowing multiple counts on the same relationship:
const posts = await Post.createQuery()
.withCount(
'comments',
{
'comments as pending_comments_count': (query) => {
query.where('approved', false);
}
}
).get();
console.log(posts[0].GetAttribute('comments_count'));
console.log($posts[0].GetAttribute('pending_comments_count'));Deferred Count Loading
Using the loadCount method, you may load a relationship count after the parent model has already been retrieved:
const book = Book.createQuery().first();
book.LoadCount('genres');If you need to set additional query constraints on the count query, you may pass an array keyed by the relationships you wish to count. The array values should be closures which receive the query builder instance:
book.LoadCount({
'reviews': function (query) {
query.where('rating', 5);
}
})Relationship Counting and Custom Select Statements
If you're combining withCount with a select statement, ensure that you call withCount after the select method:
posts = await Post.createQuery().select(['title', 'body'])
.withCount('comments')
.get();Other Aggregate Functions
In addition to the withCount method, Eloquent provides withMin, withMax, withAvg, withSum, and withExists methods. These methods will place a {relation}_{function}_{column} attribute on your resulting models:
const posts = await Post.createQuery().withSum('comments', 'votes').get();
for (const post of posts) {
console.log(post.GetAttribute('comments_sum_votes'));
}If you wish to access the result of the aggregate function using another name, you may specify your own alias:
const posts = await Post.createQuery().withSum('comments as total_comments', 'votes').get();
for (const pos of posts) {
console.log(post.GetAttribute('total_comments'));
}Like the loadCount method, deferred versions of these methods are also available. These additional aggregate operations may be performed on Eloquent models that have already been retrieved:
const post = await Post.createQuery().first();
post.LoadSum('comments', 'votes');If you're combining these aggregate methods with a select statement, ensure that you call the aggregate methods after the select method:
const posts = Post.createQuery().select(['title', 'body'])
.withExists('comments')
.get();Counting Related Models on Morph To Relationships
If you would like to eager load a "morph to" relationship, as well as related model counts for the various entities that may be returned by that relationship, you may utilize the with method in combination with the morphTo relationship's morphWithCount method.
In this example, let's assume that Photo and Post models may create ActivityFeed models. We will assume the ActivityFeed model defines a "morph to" relationship named parentable that allows us to retrieve the parent Photo or Post model for a given ActivityFeed instance. Additionally, let's assume that Photo models "have many" Tag models and Post models "have many" Comment models.
Now, let's imagine we want to retrieve ActivityFeed instances and eager load the parentable parent models for each ActivityFeed instance. In addition, we want to retrieve the number of tags that are associated with each parent photo and the number of comments that are associated with each parent post:
const activities = await ActivityFeed.createQuery().with({
'parentable': function (morphTo) {
morphTo.morphWithCount({
"Photo": ['tags'],
'Post': ['comments'],
});
}
}).get();Deferred Count Loading
Let's assume we have already retrieved a set of ActivityFeed models and now we would like to load the nested relationship counts for the various parentable models associated with the activity feeds. You may use the LoadMorphCount method to accomplish this:
const activities = await ActivityFeed.createQuery().with('parentable').get();
activities.LoadMorphCount('parentable', {
'Photo': ['tags'],
'Post': ['comments'],
});