composer require watson/aggregate in your Laravel project. No additional configuration is required due to Laravel's autodiscovery feature.withCount() with withSum(), withAvg(), withMin(), or withMax() for relationships where you need aggregated values (e.g., total revenue, average rating, or max price).
$users = User::withSum('orders', 'amount')->get();
$users->first()->orders_sum; // Access the aggregated value
withCount() behavior, as aggregate extends this functionality.Basic Aggregation:
Replace withCount() with aggregate methods for relationships:
// Count vs. Sum
$posts = Post::withCount('comments')->get(); // comments_count
$posts = Post::withSum('comments', 'likes')->get(); // comments_sum
Aliasing Aggregates:
Use as to customize attribute names (avoid naming conflicts):
$products = Product::withSum('reviews as avg_rating', 'rating')->get();
$products->first()->avg_rating; // Access via alias
Combining Aggregates: Chain multiple aggregates in a single query:
$orders = Order::withCount('items')
->withSum('items as total_value', 'price')
->withAvg('items as avg_price', 'price')
->get();
Conditional Aggregation: Use query scopes or closures to filter aggregated data:
$activeUsers = User::where('active', true)
->withSum('orders as total_spent', 'amount')
->get();
Joins with Aggregates: Combine aggregates with joins (e.g., for multi-table relationships):
$posts = Post::join('comments', 'posts.id', '=', 'comments.post_id')
->withSum('comments as total_likes', 'likes')
->select('posts.*')
->get();
$users = User::withSum('orders', 'amount')->get(); // Eager-loaded
with() to include aggregates in API responses:
return User::with(['orders' => function ($query) {
$query->select('user_id', \DB::raw('SUM(amount) as total'));
}])->get();
$cachedUsers = Cache::remember('users_with_aggregates', now()->addHours(1), function () {
return User::withSum('orders', 'amount')->get();
});
Column Existence:
SQLSTATE[42S22]: Column not found if the aggregated column doesn’t exist in the related table.withSum('products', 'price') requires a price column in the products table).Reserved Keywords:
order, group) as column names may break queries.withSum('orders as total_order_value', 'amount')
Null Values:
SUM ignore NULL values, while AVG treats them as 0. MIN/MAX return NULL if all values are NULL.NULL in your application logic:
$total = $user->orders_sum ?? 0;
Overwriting Attributes:
created_at).withSum('orders as order_total_amount', 'amount')
Complex Relationships:
hasMany). Polymorphic or nested relationships may require raw queries.with() with a closure for custom logic:
User::with(['orders' => function ($query) {
$query->select(\DB::raw('user_id, SUM(amount) as total'));
}])->get();
\DB::enableQueryLog();
$users = User::withSum('orders', 'amount')->get();
dd(\DB::getQueryLog());
$results = DB::table('users')
->leftJoin('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', \DB::raw('SUM(orders.amount) as total_orders'))
->groupBy('users.id')
->get();
Custom Aggregates:
withStdDev).php artisan vendor:publish --provider="Watson\Aggregate\AggregateServiceProvider".boot() method.Global Scopes:
class OrderScope implements \Illuminate\Database\Eloquent\Scope
{
public function apply(\Illuminate\Database\Eloquent\Builder $builder, \Illuminate\Database\Model $model)
{
$builder->withSum('items', 'price as total_price');
}
}
Register the scope in your Order model’s $with property:
protected static $with = ['orderScope'];
Testing:
$user = User::withSum('orders', 'amount')->first();
$this->assertEquals(100, $user->orders_sum);
DB::shouldReceive() to stub queries:
DB::shouldReceive('select')
->once()
->andReturn([/* mock data */]);
How can I help you explore Laravel packages today?