Introduction to Collections
Collections are one of Laravel's most powerful features. They provide a fluent, convenient wrapper for working with arrays of data. Think of collections as arrays on steroids - they offer dozens of methods for filtering, mapping, reducing, and transforming data in elegant ways.
Note: Every Eloquent query that returns multiple results returns a Collection instance. You can also create collections manually from any array of data.
Creating Collections
There are multiple ways to create collections:
<?php
use Illuminate\Support\Collection;
// From array using collect() helper
$collection = collect([1, 2, 3, 4, 5]);
// Using Collection class
$collection = new Collection(['apple', 'banana', 'orange']);
// From Eloquent query (automatic)
$users = User::all(); // Returns Collection
$posts = Post::where('published', true)->get(); // Returns Collection
// Empty collection
$empty = collect();
// From range
$numbers = collect(range(1, 10));
Basic Collection Methods
Let's explore fundamental collection methods:
<?php
$collection = collect([1, 2, 3, 4, 5]);
// all() - Get all items as array
$array = $collection->all();
// [1, 2, 3, 4, 5]
// count() - Count items
$count = $collection->count();
// 5
// first() - Get first item
$first = $collection->first();
// 1
// last() - Get last item
$last = $collection->last();
// 5
// isEmpty() - Check if empty
$isEmpty = $collection->isEmpty();
// false
// isNotEmpty() - Check if not empty
$isNotEmpty = $collection->isNotEmpty();
// true
Transforming Collections with map()
The map() method transforms each item in the collection:
<?php
// Double each number
$numbers = collect([1, 2, 3, 4, 5]);
$doubled = $numbers->map(function ($number) {
return $number * 2;
});
// [2, 4, 6, 8, 10]
// Using arrow functions (PHP 7.4+)
$doubled = $numbers->map(fn($n) => $n * 2);
// Map user objects to names
$users = User::all();
$names = $users->map(function ($user) {
return $user->name;
});
// Map with key and value
$products = collect([
['name' => 'Desk', 'price' => 200],
['name' => 'Chair', 'price' => 100],
]);
$formatted = $products->map(function ($product) {
return $product['name'] . ': $' . $product['price'];
});
// ['Desk: $200', 'Chair: $100']
Filtering Collections with filter()
The filter() method filters collection items based on a callback:
<?php
// Filter even numbers
$numbers = collect([1, 2, 3, 4, 5, 6]);
$even = $numbers->filter(function ($number) {
return $number % 2 === 0;
});
// [2, 4, 6]
// Filter users by role
$users = User::all();
$admins = $users->filter(function ($user) {
return $user->role === 'admin';
});
// Filter truthy values
$values = collect([1, 2, null, 4, false, 6]);
$truthy = $values->filter();
// [1, 2, 4, 6]
// reject() - opposite of filter()
$numbers = collect([1, 2, 3, 4, 5]);
$odd = $numbers->reject(function ($number) {
return $number % 2 === 0;
});
// [1, 3, 5]
Extracting Values with pluck()
The pluck() method retrieves values for a given key:
<?php
// Pluck names from array of arrays
$users = collect([
['name' => 'John', 'age' => 30],
['name' => 'Jane', 'age' => 25],
['name' => 'Bob', 'age' => 35],
]);
$names = $users->pluck('name');
// ['John', 'Jane', 'Bob']
// Pluck with key
$namesByAge = $users->pluck('name', 'age');
// [30 => 'John', 25 => 'Jane', 35 => 'Bob']
// Pluck from Eloquent models
$users = User::all();
$emails = $users->pluck('email');
// Pluck nested values using dot notation
$posts = collect([
['title' => 'Post 1', 'author' => ['name' => 'John']],
['title' => 'Post 2', 'author' => ['name' => 'Jane']],
]);
$authorNames = $posts->pluck('author.name');
// ['John', 'Jane']
Reducing Collections with reduce()
The reduce() method reduces a collection to a single value:
<?php
// Sum of numbers
$numbers = collect([1, 2, 3, 4, 5]);
$sum = $numbers->reduce(function ($carry, $item) {
return $carry + $item;
}, 0);
// 15
// Concatenate strings
$words = collect(['Hello', 'World', 'from', 'Laravel']);
$sentence = $words->reduce(function ($carry, $word) {
return $carry . ' ' . $word;
});
// " Hello World from Laravel"
// Build associative array
$products = collect([
['id' => 1, 'name' => 'Desk'],
['id' => 2, 'name' => 'Chair'],
]);
$indexed = $products->reduce(function ($carry, $product) {
$carry[$product['id']] = $product['name'];
return $carry;
}, []);
// [1 => 'Desk', 2 => 'Chair']
Tip: For simple summation, use the sum() method instead of reduce: $numbers->sum()
Grouping with groupBy()
The groupBy() method groups collection items by a given key:
<?php
// Group users by role
$users = collect([
['name' => 'John', 'role' => 'admin'],
['name' => 'Jane', 'role' => 'user'],
['name' => 'Bob', 'role' => 'admin'],
['name' => 'Alice', 'role' => 'user'],
]);
$grouped = $users->groupBy('role');
/*
[
'admin' => [
['name' => 'John', 'role' => 'admin'],
['name' => 'Bob', 'role' => 'admin'],
],
'user' => [
['name' => 'Jane', 'role' => 'user'],
['name' => 'Alice', 'role' => 'user'],
],
]
*/
// Group by callback
$products = collect([
['name' => 'Desk', 'price' => 200],
['name' => 'Chair', 'price' => 100],
['name' => 'Lamp', 'price' => 50],
]);
$priceRanges = $products->groupBy(function ($product) {
return $product['price'] > 100 ? 'expensive' : 'affordable';
});
Sorting Collections
Collections offer multiple sorting methods:
<?php
// sort() - Sort by value (ascending)
$numbers = collect([5, 3, 1, 4, 2]);
$sorted = $numbers->sort();
// [1, 2, 3, 4, 5]
// sortDesc() - Sort descending
$sorted = $numbers->sortDesc();
// [5, 4, 3, 2, 1]
// sortBy() - Sort by key
$users = collect([
['name' => 'John', 'age' => 30],
['name' => 'Jane', 'age' => 25],
['name' => 'Bob', 'age' => 35],
]);
$sortedByAge = $users->sortBy('age');
// sortByDesc() - Sort by key descending
$sortedByAgeDesc = $users->sortByDesc('age');
// Sort by callback
$sortedByNameLength = $users->sortBy(function ($user) {
return strlen($user['name']);
});
// Multiple sorts
$sorted = $users->sortBy('role')
->sortBy('name');
Combining Collections
Merge, concat, and combine collections:
<?php
// merge() - Merge arrays (overwrites keys)
$collection1 = collect(['a' => 1, 'b' => 2]);
$collection2 = collect(['b' => 3, 'c' => 4]);
$merged = $collection1->merge($collection2);
// ['a' => 1, 'b' => 3, 'c' => 4]
// concat() - Append values (keeps all)
$collection1 = collect([1, 2, 3]);
$collection2 = collect([4, 5, 6]);
$concatenated = $collection1->concat($collection2);
// [1, 2, 3, 4, 5, 6]
// union() - Add items without overwriting
$collection1 = collect([1 => 'a', 2 => 'b']);
$collection2 = collect([2 => 'c', 3 => 'd']);
$union = $collection1->union($collection2);
// [1 => 'a', 2 => 'b', 3 => 'd']
// flatten() - Flatten multi-dimensional array
$collection = collect([
[1, 2, 3],
[4, 5, 6],
]);
$flattened = $collection->flatten();
// [1, 2, 3, 4, 5, 6]
Unique and Duplicates
Work with unique values and find duplicates:
<?php
// unique() - Remove duplicates
$numbers = collect([1, 2, 2, 3, 3, 3, 4]);
$unique = $numbers->unique();
// [1, 2, 3, 4]
// unique() by key
$users = collect([
['name' => 'John', 'role' => 'admin'],
['name' => 'Jane', 'role' => 'user'],
['name' => 'Bob', 'role' => 'admin'],
]);
$uniqueRoles = $users->unique('role');
// duplicates() - Get duplicate values
$numbers = collect([1, 2, 2, 3, 3, 3]);
$duplicates = $numbers->duplicates();
// [2 => 2, 4 => 3, 5 => 3]
Chunking Collections
Split collections into smaller chunks:
<?php
// chunk() - Split into chunks
$collection = collect([1, 2, 3, 4, 5, 6, 7]);
$chunks = $collection->chunk(3);
/*
[
[1, 2, 3],
[4, 5, 6],
[7]
]
*/
// split() - Split into groups
$collection = collect([1, 2, 3, 4, 5]);
$groups = $collection->split(3);
/*
[
[1, 2],
[3, 4],
[5]
]
*/
// Practical example: Display in columns
$products = Product::all();
$columns = $products->chunk(ceil($products->count() / 3));
foreach ($columns as $column) {
echo '<div class="column">';
foreach ($column as $product) {
echo '<div>' . $product->name . '</div>';
}
echo '</div>';
}
Practical Collection Operations
Real-world examples combining multiple collection methods:
<?php
// Example 1: Calculate average order value by customer
$orders = collect([
['customer' => 'John', 'amount' => 100],
['customer' => 'Jane', 'amount' => 150],
['customer' => 'John', 'amount' => 200],
['customer' => 'Jane', 'amount' => 250],
]);
$averages = $orders->groupBy('customer')
->map(function ($customerOrders) {
return $customerOrders->avg('amount');
});
// ['John' => 150, 'Jane' => 200]
// Example 2: Get top 5 most viewed posts
$posts = Post::all();
$topPosts = $posts->sortByDesc('views')
->take(5)
->pluck('title', 'id');
// Example 3: Calculate statistics
$numbers = collect([10, 20, 30, 40, 50]);
$stats = [
'sum' => $numbers->sum(),
'avg' => $numbers->avg(),
'min' => $numbers->min(),
'max' => $numbers->max(),
'median' => $numbers->median(),
];
// Example 4: Transform and filter user data
$users = User::all();
$activeUserEmails = $users
->filter(fn($user) => $user->is_active)
->sortBy('created_at')
->pluck('email')
->unique()
->values();
Lazy Collections
For large datasets, use lazy collections to reduce memory usage:
<?php
// Regular collection - loads all into memory
$users = User::all(); // Loads 10,000 users
// Lazy collection - processes one at a time
$users = User::cursor(); // Returns LazyCollection
// Process huge dataset efficiently
$users->filter(function ($user) {
return $user->is_active;
})->each(function ($user) {
// Process one user at a time
Mail::to($user)->send(new Newsletter);
});
// Create lazy collection from generator
$lazyCollection = LazyCollection::make(function () {
$file = fopen('large-file.csv', 'r');
while (($line = fgets($file)) !== false) {
yield $line;
}
fclose($file);
});
$lazyCollection->chunk(100)
->each(function ($chunk) {
// Process 100 lines at a time
});
Tip: Use lazy collections when working with large datasets (10,000+ items) or when reading large files to avoid memory issues.
Higher Order Messages
Collections support "higher order messages" for common operations:
<?php
$users = User::all();
// Instead of:
$names = $users->map(function ($user) {
return $user->name;
});
// Use higher order message:
$names = $users->map->name;
// More examples:
$users->each->markAsActive();
$posts->filter->isPublished();
$orders->sum->total;
$products->sortBy->price;
Exercise 1: E-commerce Analytics
Use collections to analyze order data and calculate key metrics.
- Create collection of orders with: customer_id, product_id, quantity, price
- Calculate total revenue: sum of (quantity * price)
- Find average order value
- Group orders by customer and calculate each customer's total spend
- Identify top 3 customers by total spend
- Count unique products ordered
- Find most popular product (highest total quantity sold)
Exercise 2: Student Grade Processing
Process student grades and generate statistics using collections.
- Create collection of students with: name, subject, grade (0-100)
- Calculate class average for each subject
- Find students with grade >= 90 (A students)
- Group students by grade level (A: 90-100, B: 80-89, etc.)
- Sort students by grade (highest to lowest)
- Get list of students who failed (grade < 60)
- Calculate median grade per subject
Exercise 3: Blog Post Dashboard
Build a dashboard with blog post statistics using collections.
- Fetch all posts with: title, author_id, category, views, created_at
- Group posts by category and count posts per category
- Find top 5 most viewed posts
- Calculate average views per author
- Get posts published in the last 30 days
- Create "trending" list: posts with >1000 views in last 7 days
- Find authors who published >5 posts this month
Summary
In this lesson, you learned:
- Creating and working with Laravel Collections
- Transforming data with map(), filter(), and reduce()
- Extracting values with pluck()
- Grouping and sorting collections
- Combining collections with merge(), concat(), flatten()
- Finding unique values and duplicates
- Chunking collections for pagination/processing
- Real-world collection patterns and use cases
- Lazy collections for memory efficiency
- Higher order messages for cleaner code
Next Lesson: We'll explore error handling, logging, and debugging techniques in Laravel.