Laravel 集合
简介
Illuminate\Support\Collection
类为处理数组数据提供了流式、方便的封装。例如,查看下面的代码,我们使用辅助函数 collect
创建一个新的集合实例,为每一个元素运行 strtoupper 函数,然后移除所有空元素:
$collection = collect(['taylor', 'abigail', null])->map(function ($name) {
return strtoupper($name);
})->reject(function ($name) {
return empty($name);
});
正如你所看到的,Collection 类允许你使用方法链对底层数组执行匹配和移除操作,通常,每个 Collection 方法都会返回一个新的 Collection 实例。
创建集合
正如上面所提到的,辅助函数 collect
返回一个新的 Illuminate\Support\Collection
实例,所以说,创建集合是很简单的:
$collection = collect([1, 2, 3]);
注:默认情况下,Eloquent 查询的结果总是返回 Collection 实例。
扩展集合
集合是可扩展的,这意味着我们可以在运行时动态添加方法到 Collection
类,例如,下面的代码给Collection
类添加了 toUpper
方法:
use Illuminate\Support\Str;
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
// ['FIRST', 'SECOND']
通常,我们需要在 service provider 中声明集合宏。
集合方法
本文档接下来的部分将会介绍 Collection 类上每一个有效的方法,所有这些方法都可以以方法链的方式流式操作底层数组。此外,几乎每个方法返回一个新的 Collection 实例,从而允许你在必要的时候保持原来的集合备份。
方法列表
- all
- average
- avg
- chunk
- collapse
- collect
- combine
- concat
- contains
- containsStrict
- count
- countBy
- crossJoin
- dd
- diff
- diffAssoc
- diffKeys
- dump
- duplicates
- duplicatesStrict
- each
- eachSpread
- every
- except
- filter
- first
- firstWhere
- flatMap
- flatten
- flip
- forget
- forPage
- get
- groupBy
- has
- implode
- intersect
- intersectByKeys
- isEmpty
- isNotEmpty
- join
- keyBy
- keys
- last
- macro
- make
- map
- mapInto
- mapSpread
- mapToGroups
- mapWithKeys
- max
- median
- merge
- mergeRecursive
- min
- mode
- nth
- only
- pad
- partition
- pipe
- pluck
- pop
- prepend
- pull
- push
- put
- random
- reduce
- reject
- replace
- replaceRecursive
- reverse
- search
- shift
- shuffle
- skip
- skipUntil
- skipWhile
- slice
- some
- sort
- sortBy
- sortByDesc
- sortDesc
- sortKeys
- sortKeysDesc
- splice
- split
- sum
- take
- takeUntil
- takeWhile
- tap
- times
- toArray
- toJson
- transform
- union
- unique
- uniqueStrict
- unless
- unlessEmpty
- unlessNotEmpty
- unwrap
- values
- when
- whenEmpty
- whenNotEmpty
- where
- whereStrict
- whereBetween
- whereIn
- whereInStrict
- whereInstanceOf
- whereNotBetween
- whereNotIn
- whereNotInStrict
- whereNotNull
- whereNull
- wrap
- zip
高阶消息传递
集合还支持“高阶消息传递”,也就是在集合上执行通用的功能,支持高阶消息传递的方法包括:average、avg、contains、each、every、filter、first、flatMap、groupBy、keyBy、map、max、min、partition、reject、some、sortBy、sortByDesc、sum 和 unique。
每个高阶消息传递都可以在集合实例上以动态属性的方式访问,例如,我们使用 each 高阶消息传递来在集合的每个对象上调用一个方法:
$users = User::where('votes', '>', 500)->get();
$users->each->markAsVip();
类似的,我们可以使用 sum 高阶消息传递来聚合用户集合的投票总数:
$users = User::where('group', 'Development')->get();
return $users->sum->votes;
懒惰集合
简介
注:在具体了解懒集合之前,建议先花点时间熟悉下 PHP 生成器。
为了继续完善功能已经很强大的 Collection 类,LazyCollection 类使用了 PHP 的生成器,从而可以通过极低的内存处理极大的数据集。
例如,假设你的应用需要通过 Laravel 提供的集合方法来解析并处理几个 GB 大小的日志文件,这个时候就可以使用懒集合(LazyCollection),它不会一次性将整个文件读入内存,而是每次只读取文件的一小部分:
use App\LogEntry;
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
$handle = fopen('log.txt', 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
})->chunk(4)->map(function ($lines) {
return LogEntry::fromLines($lines);
})->each(function (LogEntry $logEntry) {
// Process the log entry...
});
或者,假设我们需要迭代产生 10000 个 Eloquent 模型实例,使用传统的 Laravel 集合,所有 10000 个 Eloquent 模型实例必须一次性加载到内存中:
$users = App\User::all()->filter(function ($user) {
return $user->id > 500;
});
而现在,查询构建器的 cursor 方法会返回一个 LazyCollection 实例,这样一来,我们仍然只需对数据库做一次查询,但是一次只会加载一个 Eloquent 模型实例到内存。在这个例子中,filter 回调只有在迭代到每个独立用户时才会执行,从而大幅降低对内存的占用:
$users = App\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
创建懒惰集合
要创建一个懒惰集合实例,需要传递一个 PHP 生成器函数到集合的 make 方法:
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
$handle = fopen('log.txt', 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
});
Enumerable 接口
几乎所有的 Collection 类方法都对 LazyCollection 类有效,因为这两个类都实现了 Illuminate\Support\Enumerable 接口,该接口中声明了如下方法::
- all
- average
- avg
- chunk
- collapse
- collect
- combine
- concat
- contains
- containsStrict
- count
- countBy
- crossJoin
- dd
- diff
- diffAssoc
- diffKeys
- dump
- duplicates
- duplicatesStrict
- each
- eachSpread
- every
- except
- filter
- first
- firstWhere
- flatMap
- flatten
- flip
- forPage
- get
- groupBy
- has
- implode
- intersect
- intersectByKeys
- isEmpty
- isNotEmpty
- join
- keyBy
- keys
- last
- macro
- make
- map
- mapInto
- mapSpread
- mapToGroups
- mapWithKeys
- max
- median
- merge
- mergeRecursive
- min
- mode
- nth
- only
- pad
- partition
- pipe
- pluck
- random
- reduce
- reject
- replace
- replaceRecursive
- reverse
- search
- shuffle
- skip
- slice
- some
- sort
- sortBy
- sortByDesc
- sortKeys
- sortKeysDesc
- split
- sum
- take
- tap
- times
- toArray
- toJson
- union
- unique
- uniqueStrict
- unless
- unlessEmpty
- unlessNotEmpty
- unwrap
- values
- when
- whenEmpty
- whenNotEmpty
- where
- whereStrict
- whereBetween
- whereIn
- whereInStrict
- whereInstanceOf
- whereNotBetween
- whereNotIn
- whereNotInStrict
- wrap
- zip
注:改变集合的方法(例如 shift、pop、prepend 等)在 LazyCollection 类中不可用。
懒惰集合方法
除了定义在 Enumerable 契约中的方法,LazyCollection 类还包含以下方法:
tapEach()
each 方法会为每个集合项立即调用给定回调,tapEach 方法则只有在这些集合项从列表中一个一个拉取出来时才会调用给定回调:
$lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) {
dump($value);
});
// Nothing has been dumped so far...
$array = $lazyCollection->take(3)->all();
// 1
// 2
// 3
remember()
remember 方法返回一个新的懒集合,其中包含了所有已经枚举过的值,然后再次枚举的时候不会再从集合中获取它们:
$users = User::cursor()->remember();
// No query has been executed yet...
$users->take(5)->all();
// The query has been executed and the first 5 users have been hydrated from the database...
$users->take(20)->all();
// First 5 users come from the collection's cache... The rest are hydrated from the database.