Laravel 13 集合高级操作

Laravel 集合提供了丰富的方法来处理数组数据,本文将深入探讨 Laravel 13 集合的高级操作技巧。

创建集合

基本创建方式

1
2
3
4
5
6
7
8
9
10
11
use Illuminate\Support\Collection;

$collection = collect([1, 2, 3, 4, 5]);

$collection = Collection::make([1, 2, 3]);

$collection = Collection::times(5, fn ($number) => $number * 2);
// [2, 4, 6, 8, 10]

$collection = Collection::range(1, 10);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

从模型创建

1
2
$users = User::all();
$activeUsers = User::where('active', true)->get();

高阶消息传递

高阶方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$users = User::all();

// each
$users->each->markAsActive();
$users->each(fn ($user) => $user->save());

// map
$names = $users->map->name;
$emails = $users->map->email;

// filter
$active = $users->filter->isActive();
$admins = $users->filter->isAdmin();

// reject
$nonAdmins = $users->reject->isAdmin();

// sum
$total = Order::all()->sum->total;

// avg
$average = Product::all()->avg->price;

// unique
$unique = $users->unique->email;

链式操作

复杂链式调用

1
2
3
4
5
6
7
$result = collect($orders)
->filter(fn ($order) => $order->status === 'completed')
->map(fn ($order) => $order->total)
->reject(fn ($total) => $total <= 0)
->sort()
->take(10)
->values();

分组和聚合

groupBy

1
2
3
4
5
6
7
8
9
10
11
12
13
$users = collect([
['name' => 'John', 'department' => 'Sales'],
['name' => 'Jane', 'department' => 'Marketing'],
['name' => 'Bob', 'department' => 'Sales'],
]);

$grouped = $users->groupBy('department');
// [
// 'Sales' => [['name' => 'John', 'department' => 'Sales'], ...],
// 'Marketing' => [['name' => 'Jane', 'department' => 'Marketing']],
// ]

$grouped = $users->groupBy(fn ($user) => strtoupper($user['department'][0]));

多级分组

1
2
3
4
5
6
7
8
9
10
$employees = collect([
['name' => 'John', 'department' => 'Sales', 'city' => 'NYC'],
['name' => 'Jane', 'department' => 'Sales', 'city' => 'LA'],
['name' => 'Bob', 'department' => 'Marketing', 'city' => 'NYC'],
]);

$grouped = $employees->groupBy([
'department',
fn ($employee) => $employee['city'],
]);

keyBy

1
2
3
4
5
6
7
$users = collect([
['id' => 1, 'name' => 'John'],
['id' => 2, 'name' => 'Jane'],
]);

$keyed = $users->keyBy('id');
// [1 => ['id' => 1, 'name' => 'John'], 2 => ['id' => 2, 'name' => 'Jane']]

聚合方法

1
2
3
4
5
6
7
8
9
10
11
12
$numbers = collect([1, 2, 3, 4, 5]);

$sum = $numbers->sum(); // 15
$avg = $numbers->avg(); // 3
$min = $numbers->min(); // 1
$max = $numbers->max(); // 5
$median = $numbers->median(); // 3
$mode = $numbers->mode(); // [1, 2, 3, 4, 5]

// 条件聚合
$total = Order::all()->sum->total;
$average = Product::all()->avg->price;

数据转换

mapWithKeys

1
2
3
4
5
6
7
8
9
$users = collect([
['id' => 1, 'name' => 'John'],
['id' => 2, 'name' => 'Jane'],
]);

$pairs = $users->mapWithKeys(fn ($user) => [
$user['id'] => $user['name']
]);
// [1 => 'John', 2 => 'Jane']

mapInto

1
2
3
4
5
6
class User
{
public function __construct(public string $name) {}
}

$users = collect(['John', 'Jane', 'Bob'])->mapInto(User::class);

mapSpread

1
2
3
4
5
6
7
$collection = collect([
['John', 'Doe'],
['Jane', 'Smith'],
]);

$fullNames = $collection->mapSpread(fn ($first, $last) => "$first $last");
// ['John Doe', 'Jane Smith']

mapToGroups

1
2
3
4
5
6
7
8
9
10
$users = collect([
['name' => 'John', 'role' => 'admin'],
['name' => 'Jane', 'role' => 'user'],
['name' => 'Bob', 'role' => 'admin'],
]);

$grouped = $users->mapToGroups(fn ($user) => [
$user['role'] => $user['name']
]);
// ['admin' => ['John', 'Bob'], 'user' => ['Jane']]

pluck

1
2
3
4
5
6
7
$users = collect([
['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'],
]);

$names = $users->pluck('name'); // ['John', 'Jane']
$emails = $users->pluck('email', 'id'); // [1 => 'john@example.com', 2 => 'jane@example.com']

过滤和搜索

where 条件

1
2
3
4
5
6
7
8
9
$users = collect([
['name' => 'John', 'age' => 30, 'active' => true],
['name' => 'Jane', 'age' => 25, 'active' => false],
['name' => 'Bob', 'age' => 35, 'active' => true],
]);

$active = $users->where('active', true);
$older = $users->where('age', '>=', 30);
$johns = $users->where('name', 'John');

whereIn / whereNotIn

1
2
$filtered = $users->whereIn('age', [25, 30]);
$excluded = $users->whereNotIn('age', [35]);

whereBetween

1
2
$inRange = $users->whereBetween('age', [25, 35]);
$outRange = $users->whereNotBetween('age', [30, 40]);

whereNull / whereNotNull

1
2
$withEmail = $users->whereNotNull('email');
$withoutEmail = $users->whereNull('email');

first / firstWhere / firstOrFail

1
2
3
4
$first = $users->first();
$john = $users->firstWhere('name', 'John');
$adult = $users->first(fn ($user) => $user['age'] >= 18);
$required = $users->firstOrFail(fn ($user) => $user['id'] === 999);
1
2
3
$position = collect([1, 2, 3, 4, 5])->search(3); // 2
$position = collect(['John', 'Jane'])->search('Jane'); // 1
$position = collect([1, 2, 3])->search(fn ($value) => $value > 2); // 2

排序

sort / sortBy

1
2
3
4
5
6
7
8
9
10
11
12
$numbers = collect([3, 1, 4, 1, 5, 9]);
$sorted = $numbers->sort(); // [1, 1, 3, 4, 5, 9]
$desc = $numbers->sortDesc(); // [9, 5, 4, 3, 1, 1]

$users = collect([
['name' => 'John', 'age' => 30],
['name' => 'Jane', 'age' => 25],
]);

$byAge = $users->sortBy('age');
$byName = $users->sortBy('name');
$byAgeDesc = $users->sortByDesc('age');

多条件排序

1
2
3
4
5
6
7
8
9
10
$users = collect([
['name' => 'John', 'age' => 30],
['name' => 'Jane', 'age' => 25],
['name' => 'Bob', 'age' => 30],
]);

$sorted = $users->sortBy([
['age', 'desc'],
['name', 'asc'],
]);

自定义排序

1
$sorted = $users->sortBy(fn ($a, $b) => $a['age'] <=> $b['age']);

分页和分割

chunk

1
2
3
4
$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);

$chunks = $collection->chunk(3);
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

chunkWhile

1
2
3
4
$collection = collect(['A', 'A', 'B', 'B', 'B', 'C']);

$chunks = $collection->chunkWhile(fn ($value, $key, $chunk) => $value === $chunk->last());
// [['A', 'A'], ['B', 'B', 'B'], ['C']]

split

1
2
3
4
$collection = collect([1, 2, 3, 4, 5, 6]);

$split = $collection->split(3);
// [[1, 2], [3, 4], [5, 6]]

partition

1
2
3
4
$users = collect([1, 2, 3, 4, 5, 6]);

[$even, $odd] = $users->partition(fn ($n) => $n % 2 === 0);
// $even = [2, 4, 6], $odd = [1, 3, 5]

take / takeWhile / takeUntil

1
2
3
4
5
6
7
$collection = collect([1, 2, 3, 4, 5]);

$first3 = $collection->take(3); // [1, 2, 3]
$last2 = $collection->take(-2); // [4, 5]

$takeWhile = $collection->takeWhile(fn ($n) => $n < 4); // [1, 2, 3]
$takeUntil = $collection->takeUntil(fn ($n) => $n === 4); // [1, 2, 3]

skip / skipWhile / skipUntil

1
2
3
4
5
$collection = collect([1, 2, 3, 4, 5]);

$skip2 = $collection->skip(2); // [3, 4, 5]
$skipWhile = $collection->skipWhile(fn ($n) => $n < 3); // [3, 4, 5]
$skipUntil = $collection->skipUntil(fn ($n) => $n === 4); // [4, 5]

集合运算

合并

1
2
3
4
5
$a = collect([1, 2, 3]);
$b = collect([3, 4, 5]);

$merged = $a->merge($b); // [1, 2, 3, 3, 4, 5]
$union = $a->union($b); // [1, 2, 3, 4, 5]

交集和差集

1
2
3
4
5
6
7
$a = collect([1, 2, 3, 4]);
$b = collect([3, 4, 5, 6]);

$intersect = $a->intersect($b); // [3, 4]
$diff = $a->diff($b); // [1, 2]
$diffAssoc = $a->diffAssoc([1 => 'a', 2 => 'b', 3 => 'c']);
$diffKeys = $a->diffKeys([1 => 'a', 5 => 'e']);

唯一值

1
2
3
4
5
6
7
8
9
10
11
$collection = collect([1, 1, 2, 2, 3, 3, 4, 4]);
$unique = $collection->unique(); // [1, 2, 3, 4]

$users = collect([
['id' => 1, 'name' => 'John'],
['id' => 1, 'name' => 'John'],
['id' => 2, 'name' => 'Jane'],
]);

$unique = $users->unique('id');
$unique = $users->unique(fn ($user) => $user['id'] . $user['name']);

数学运算

1
2
3
4
5
6
7
8
9
10
11
12
$numbers = collect([1, 2, 3, 4, 5]);

$sum = $numbers->sum();
$avg = $numbers->avg();
$min = $numbers->min();
$max = $numbers->max();
$median = $numbers->median();
$mode = $numbers->mode();
$count = $numbers->count();

// 条件计算
$sum = $numbers->filter(fn ($n) => $n > 2)->sum();

懒惰集合

创建懒惰集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
for ($i = 0; $i < 1000000; $i++) {
yield $i;
}
});

// 从生成器创建
function readLines($path) {
$file = fopen($path, 'r');
while ($line = fgets($file)) {
yield $line;
}
fclose($file);
}

$lazy = LazyCollection::make(fn () => readLines('large-file.txt'));

懒惰集合操作

1
2
3
4
5
LazyCollection::times(INF)
->take(1000)
->filter(fn ($n) => $n % 2 === 0)
->take(10)
->each(fn ($n) => print($n . PHP_EOL));

内存高效处理

1
2
3
User::cursor()
->filter(fn ($user) => $user->active)
->each(fn ($user) => $user->sendNotification());

实用技巧

扁平化嵌套

1
2
3
4
5
6
7
$nested = collect([
['name' => 'John', 'tags' => ['php', 'laravel']],
['name' => 'Jane', 'tags' => ['vue', 'react']],
]);

$tags = $nested->pluck('tags')->flatten()->unique();
// ['php', 'laravel', 'vue', 'react']

条件执行

1
2
3
4
5
6
7
$collection = collect([1, 2, 3, 4, 5]);

$result = $collection->when(true, fn ($col) => $col->push(6));
// [1, 2, 3, 4, 5, 6]

$result = $collection->unless(false, fn ($col) => $col->push(7));
// [1, 2, 3, 4, 5, 6, 7]

管道操作

1
2
3
4
$result = collect([1, 2, 3, 4, 5])
->pipe(fn ($collection) => $collection->map(fn ($n) => $n * 2))
->pipe(fn ($collection) => $collection->filter(fn ($n) => $n > 4));
// [6, 8, 10]

转换为数组和 JSON

1
2
3
4
5
$collection = collect(['name' => 'John', 'age' => 30]);

$array = $collection->toArray();
$json = $collection->toJson();
$all = $collection->all();

最佳实践

1. 使用高阶消息传递简化代码

1
2
3
4
5
6
7
// 好的做法
$users->each->save();
$names = $users->map->name;

// 不好的做法
$users->each(fn ($user) => $user->save());
$names = $users->map(fn ($user) => $user->name);

2. 使用懒惰集合处理大数据

1
2
3
4
5
// 好的做法
User::cursor()->each(fn ($user) => $user->process());

// 不好的做法
User::all()->each(fn ($user) => $user->process());

3. 链式操作保持可读性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 好的做法
$result = $orders
->filter(fn ($order) => $order->completed)
->map(fn ($order) => $order->total)
->sum();

// 不好的做法
$filtered = [];
foreach ($orders as $order) {
if ($order->completed) {
$filtered[] = $order->total;
}
}
$result = array_sum($filtered);

总结

Laravel 集合提供了强大且优雅的数据处理能力。通过合理使用高阶消息传递、链式操作、懒惰集合等特性,可以编写出简洁、高效、可读性强的代码。记住在处理大数据集时使用懒惰集合来优化内存使用,并充分利用集合的丰富方法来简化数据处理逻辑。