集合

介绍

Illuminate\Support\Collection 类为操作数组数据提供了一套流畅、便捷的封装。看看下面这段代码——先用 collect() 辅助函数从数组创建一个集合实例,然后对每个元素执行 strtoupper,最后去掉所有空元素:

$collection = collect(['Taylor', 'Abigail', null])->map(function (?string $name) {
    return strtoupper($name);
})->reject(function (string $name) {
    return empty($name);
});

可以看到,Collection 类允许你把方法串起来,对底层数组做流畅的映射和过滤。一般来说,集合是不可变的(immutable),每个集合方法都会返回一个全新的 Collection 实例。

创建集合

前面提到,collect() 辅助函数会根据传入的数组返回一个新的 Illuminate\Support\Collection 实例。所以创建集合很简单:

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

你也可以用 make 和 fromJson 方法来创建集合。

信息

Eloquent 查询的结果始终以 Collection 实例返回。

扩展集合

集合是「可宏化」的(macroable),你可以在运行时给 Collection 类添加额外的方法。Illuminate\Support\Collection 类的 macro() 方法接收一个闭包,该闭包在你调用宏时执行。闭包内部可以通过 $this 访问集合的其他方法,就像它是集合类的真正方法一样。比如,下面的代码给 Collection 类添加了一个 toUpper 方法:

use Illuminate\Support\Collection;
use Illuminate\Support\Str;

Collection::macro('toUpper', function () {
    return $this->map(function (string $value) {
        return Str::upper($value);
    });
});

$collection = collect(['first', 'second']);

$upper = $collection->toUpper();

// ['FIRST', 'SECOND']

通常,你应该在服务提供器的 boot 方法中声明集合宏。

宏参数

如果有需要,你可以定义接收额外参数的宏:

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Lang;

Collection::macro('toLocale', function (string $locale) {
    return $this->map(function (string $value) use ($locale) {
        return Lang::get($value, [], $locale);
    });
});

$collection = collect(['first', 'second']);

$translated = $collection->toLocale('es');

// ['primero', 'segundo'];

可用方法

接下来的文档会逐一介绍 Collection 类上可用的每个方法。记住,所有这些方法都可以串起来流畅地操作底层数组。而且几乎所有方法都返回一个新的 Collection 实例,让你在需要时可以保留原始集合不变:

after all average avg
before chunk chunkWhile collapse
collapseWithKeys collect combine concat
contains containsStrict count countBy
crossJoin dd diff diffAssoc
diffAssocUsing diffKeys doesntContain doesntContainStrict
dot dump duplicates duplicatesStrict
each eachSpread ensure every
except filter first firstOrFail
firstWhere flatMap flatten flip
forget forPage fromJson get
groupBy has hasAny hasMany
hasSole implode intersect intersectUsing
intersectAssoc intersectAssocUsing intersectByKeys isEmpty
isNotEmpty join keyBy keys
last lazy macro make
map mapInto mapSpread mapToGroups
mapWithKeys max median merge
mergeRecursive min mode multiply
nth only pad partition
percentage pipe pipeInto pipeThrough
pluck pop prepend pull
push put random range
reduce reduceInto reduceSpread reject
replace replaceRecursive reverse search
select shift shuffle skip
skipUntil skipWhile slice sliding
sole some sort sortBy
sortByDesc sortDesc sortKeys sortKeysDesc
sortKeysUsing splice split splitIn
sum take takeUntil takeWhile
tap times toArray toJson
toPrettyJson transform undot union
unique uniqueStrict unless unlessEmpty
unlessNotEmpty unwrap value values
when whenEmpty whenNotEmpty where
whereStrict whereBetween whereIn whereInStrict
whereInstanceOf whereNotBetween whereNotIn whereNotInStrict
whereNotNull whereNull wrap zip

方法列表

after()

after() 方法返回给定元素后面的那个元素。如果给定的元素不存在或是最后一个元素,则返回 null:

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

$collection->after(3);

// 4

$collection->after(5);

// null

该方法搜索元素时使用「宽松比较」,也就是说,包含整数值的字符串会和相同值的整数被视为相等。如果要使用「严格比较」,可以传入 strict 参数:

collect([2, 4, 6, 8])->after('4', strict: true);

// null

你也可以传入闭包来查找第一个满足条件的元素,返回它后面的元素:

collect([2, 4, 6, 8])->after(function (int $item, int $key) {
    return $item > 5;
});

// 8

all()

all() 方法返回集合底层代表的数组:

collect([1, 2, 3])->all();

// [1, 2, 3]

average()

avg 方法的别名。

avg()

avg() 方法返回给定键的平均值:

$average = collect([
    ['foo' => 10],
    ['foo' => 10],
    ['foo' => 20],
    ['foo' => 40]
])->avg('foo');

// 20

$average = collect([1, 1, 2, 4])->avg();

// 2

before()

before() 方法与 after 方法相反,返回给定元素前面的那个元素。如果给定的元素不存在或是第一个元素,则返回 null:

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

$collection->before(3);

// 2

$collection->before(1);

// null

collect([2, 4, 6, 8])->before('4', strict: true);

// null

collect([2, 4, 6, 8])->before(function (int $item, int $key) {
    return $item > 5;
});

// 4

chunk()

chunk() 方法将集合拆分成多个指定大小的较小集合:

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

$chunks = $collection->chunk(4);

$chunks->all();

// [[1, 2, 3, 4], [5, 6, 7]]

这个方法在视图中使用 Bootstrap 等栅格系统时特别有用。比如你有一组 Eloquent 模型想在栅格中展示:

@foreach ($products->chunk(3) as $chunk)
    <div class="row">
        @foreach ($chunk as $product)
            <div class="col-xs-4">{{ $product->name }}</div>
        @endforeach
    </div>
@endforeach

chunkWhile()

chunkWhile() 方法根据给定回调的求值结果将集合拆分成多个较小的集合。闭包接收的 $chunk 变量可用于检查前一个元素:

$collection = collect(str_split('AABBCCCD'));

$chunks = $collection->chunkWhile(function (string $value, int $key, Collection $chunk) {
    return $value === $chunk->last();
});

$chunks->all();

// [['A', 'A'], ['B', 'B'], ['C', 'C', 'C'], ['D']]

collapse()

collapse() 方法将一个由数组或集合组成的集合展开成一个扁平的单一集合:

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

$collapsed = $collection->collapse();

$collapsed->all();

// [1, 2, 3, 4, 5, 6, 7, 8, 9]

collapseWithKeys()

collapseWithKeys() 方法将由数组或集合组成的集合展开成一个单一集合,同时保留原始键。如果集合已经是扁平的,则返回空集合:

$collection = collect([
    ['first'  => collect([1, 2, 3])],
    ['second' => [4, 5, 6]],
    ['third'  => collect([7, 8, 9])]
]);

$collapsed = $collection->collapseWithKeys();

$collapsed->all();

// [
//     'first'  => [1, 2, 3],
//     'second' => [4, 5, 6],
//     'third'  => [7, 8, 9],
// ]

collect()

collect() 方法返回一个包含当前集合元素的新 Collection 实例:

$collectionA = collect([1, 2, 3]);

$collectionB = $collectionA->collect();

$collectionB->all();

// [1, 2, 3]

collect() 方法主要用于将惰性集合转换为标准 Collection 实例:

$lazyCollection = LazyCollection::make(function () {
    yield 1;
    yield 2;
    yield 3;
});

$collection = $lazyCollection->collect();

$collection::class;

// 'Illuminate\Support\Collection'

$collection->all();

// [1, 2, 3]

信息

当你有一个 Enumerable 实例且需要非惰性的集合实例时,collect() 方法特别有用。因为 collect() 是 Enumerable 契约的一部分,你可以安全地用它来获取 Collection 实例。

combine()

combine() 方法将集合的值作为键,与另一个数组或集合的值组合起来:

$collection = collect(['name', 'age']);

$combined = $collection->combine(['George', 29]);

$combined->all();

// ['name' => 'George', 'age' => 29]

concat()

concat() 方法将给定数组或集合的值追加到原集合的末尾:

$collection = collect(['John Doe']);

$concatenated = $collection->concat(['Jane Doe'])->concat(['name' => 'Johnny Doe']);

$concatenated->all();

// ['John Doe', 'Jane Doe', 'Johnny Doe']

concat() 方法会对追加的元素重新进行数字索引。如果需要在关联集合中保留键,请参考 merge 方法。

contains()

contains() 方法判断集合是否包含给定元素。你可以传入闭包来判断集合中是否存在满足条件的元素:

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

$collection->contains(function (int $value, int $key) {
    return $value > 5;
});

// false

也可以传入字符串来判断集合是否包含该值:

$collection = collect(['name' => 'Desk', 'price' => 100]);

$collection->contains('Desk');

// true

$collection->contains('New York');

// false

还可以传入键值对,判断集合中是否存在该键值对:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
]);

$collection->contains('product', 'Bookcase');

// false

contains() 方法在检查值时使用「宽松比较」,也就是说,包含整数值的字符串会和相同值的整数被视为相等。要用「严格比较」,请使用 containsStrict 方法。

contains() 的反向方法参见 doesntContain。

containsStrict()

此方法签名与 contains 相同,但所有值比较都使用「严格比较」。

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

count()

count() 方法返回集合中元素的总数:

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

$collection->count();

// 4

countBy()

countBy() 方法统计集合中各值的出现次数。默认情况下,它统计每个元素的出现次数,让你可以统计集合中某些「类型」的元素有多少个:

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

$counted = $collection->countBy();

$counted->all();

// [1 => 1, 2 => 3, 3 => 1]

你也可以传入闭包,按自定义的值来统计:

$collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']);

$counted = $collection->countBy(function (string $email) {
    return substr(strrchr($email, '@'), 1);
});

$counted->all();

// ['gmail.com' => 2, 'yahoo.com' => 1]

crossJoin()

crossJoin() 方法将集合的值与给定的数组或集合做交叉连接,返回所有可能排列的笛卡尔积:

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

$matrix = $collection->crossJoin(['a', 'b']);

$matrix->all();

/*
    [
        [1, 'a'],
        [1, 'b'],
        [2, 'a'],
        [2, 'b'],
    ]
*/

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

$matrix = $collection->crossJoin(['a', 'b'], ['I', 'II']);

$matrix->all();

/*
    [
        [1, 'a', 'I'],
        [1, 'a', 'II'],
        [1, 'b', 'I'],
        [1, 'b', 'II'],
        [2, 'a', 'I'],
        [2, 'a', 'II'],
        [2, 'b', 'I'],
        [2, 'b', 'II'],
    ]
*/

dd()

dd() 方法打印集合的元素并终止脚本执行:

$collection = collect(['John Doe', 'Jane Doe']);

$collection->dd();

/*
    array:2 [
        0 => "John Doe"
        1 => "Jane Doe"
    ]
*/

如果不想停止脚本执行,请使用 dump 方法。

diff()

diff() 方法基于值来比较集合与另一个集合或普通 PHP 数组,返回原集合中存在但给定集合中不存在的值:

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

$diff = $collection->diff([2, 4, 6, 8]);

$diff->all();

// [1, 3, 5]

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

diffAssoc()

diffAssoc() 方法基于键和值来比较集合与另一个集合或普通 PHP 数组,返回原集合中存在但给定集合中不存在的键值对:

$collection = collect([
    'color' => 'orange',
    'type' => 'fruit',
    'remain' => 6,
]);

$diff = $collection->diffAssoc([
    'color' => 'yellow',
    'type' => 'fruit',
    'remain' => 3,
    'used' => 6,
]);

$diff->all();

// ['color' => 'orange', 'remain' => 6]

diffAssocUsing()

与 diffAssoc 不同,diffAssocUsing 接收一个用户自定义的回调函数来进行键的比较:

$collection = collect([
    'color' => 'orange',
    'type' => 'fruit',
    'remain' => 6,
]);

$diff = $collection->diffAssocUsing([
    'Color' => 'yellow',
    'Type' => 'fruit',
    'Remain' => 3,
], 'strnatcasecmp');

$diff->all();

// ['color' => 'orange', 'remain' => 6]

回调必须是一个比较函数,返回一个小于、等于或大于零的整数。更多细节请参考 PHP 文档中的 array_diff_uassoc,这是 diffAssocUsing() 方法内部使用的 PHP 函数。

diffKeys()

diffKeys() 方法基于键来比较集合与另一个集合或普通 PHP 数组,返回原集合中存在但给定集合中不存在的键值对:

$collection = collect([
    'one' => 10,
    'two' => 20,
    'three' => 30,
    'four' => 40,
    'five' => 50,
]);

$diff = $collection->diffKeys([
    'two' => 2,
    'four' => 4,
    'six' => 6,
    'eight' => 8,
]);

$diff->all();

// ['one' => 10, 'three' => 30, 'five' => 50]

doesntContain()

doesntContain() 方法判断集合是否不包含给定元素。你可以传入闭包来判断集合中是否不存在满足条件的元素:

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

$collection->doesntContain(function (int $value, int $key) {
    return $value < 5;
});

// false

也可以传入字符串来判断集合是否不包含该值:

$collection = collect(['name' => 'Desk', 'price' => 100]);

$collection->doesntContain('Table');

// true

$collection->doesntContain('Desk');

// false

还可以传入键值对,判断集合中是否不存在该键值对:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
]);

$collection->doesntContain('product', 'Bookcase');

// true

doesntContain() 方法在检查值时使用「宽松比较」。

doesntContainStrict()

此方法签名与 doesntContain 相同,但所有值比较都使用「严格比较」。

dot()

dot() 方法将多维集合展平成一层,用「点」记法表示层级深度:

$collection = collect(['products' => ['desk' => ['price' => 100]]]);

$flattened = $collection->dot();

$flattened->all();

// ['products.desk.price' => 100]

dump()

dump() 方法打印集合的元素:

$collection = collect(['John Doe', 'Jane Doe']);

$collection->dump();

/*
    array:2 [
        0 => "John Doe"
        1 => "Jane Doe"
    ]
*/

如果想在打印后停止脚本执行,请使用 dd 方法。

duplicates()

duplicates() 方法从集合中检索并返回重复的值:

$collection = collect(['a', 'b', 'a', 'c', 'b']);

$collection->duplicates();

// [2 => 'a', 4 => 'b']

如果集合包含数组或对象,可以传入你想检查重复值的属性键:

$employees = collect([
    ['email' => 'abigail@example.com', 'position' => 'Developer'],
    ['email' => 'james@example.com', 'position' => 'Designer'],
    ['email' => 'victoria@example.com', 'position' => 'Developer'],
]);

$employees->duplicates('position');

// [2 => 'Developer']

duplicatesStrict()

此方法签名与 duplicates 相同,但所有值比较都使用「严格比较」。

each()

each() 方法遍历集合中的元素,将每个元素传给闭包:

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

$collection->each(function (int $item, int $key) {
    // ...
});

如果想停止遍历,可以在闭包中返回 false:

$collection->each(function (int $item, int $key) {
    if (/* condition */) {
        return false;
    }
});

eachSpread()

eachSpread() 方法遍历集合中的元素,将每个嵌套元素的值展开传给给定回调:

$collection = collect([['John Doe', 35], ['Jane Doe', 33]]);

$collection->eachSpread(function (string $name, int $age) {
    // ...
});

你可以通过在回调中返回 false 来停止遍历:

$collection->eachSpread(function (string $name, int $age) {
    return false;
});

ensure()

ensure() 方法可用于验证集合中所有元素都是给定类型或类型列表中的一种,否则会抛出 UnexpectedValueException:

return $collection->ensure(User::class);

return $collection->ensure([User::class, Customer::class]);

也可以指定原始类型,如 string、int、float、bool 和 array:

return $collection->ensure('int');

注意

ensure() 方法不保证后续不会有不同类型的元素被添加到集合中。

every()

every() 方法可用于验证集合中所有元素是否都满足给定条件:

collect([1, 2, 3, 4])->every(function (int $value, int $key) {
    return $value > 2;
});

// false

如果集合为空,every() 方法会返回 true:

$collection = collect([]);

$collection->every(function (int $value, int $key) {
    return $value > 2;
});

// true

except()

except() 方法返回集合中除了指定键之外的所有元素:

$collection = collect(['product_id' => 1, 'price' => 100, 'discount' => false]);

$filtered = $collection->except(['price', 'discount']);

$filtered->all();

// ['product_id' => 1]

except() 的反向方法参见 only。

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

filter()

filter() 方法用给定回调过滤集合,只保留满足条件的元素:

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

$filtered = $collection->filter(function (int $value, int $key) {
    return $value > 2;
});

$filtered->all();

// [3, 4]

如果不传回调,集合中所有等同于 false 的元素都会被移除:

$collection = collect([1, 2, 3, null, false, '', 0, []]);

$collection->filter()->all();

// [1, 2, 3]

filter() 的反向方法参见 reject。

first()

first() 方法返回集合中第一个满足给定条件的元素:

collect([1, 2, 3, 4])->first(function (int $value, int $key) {
    return $value > 2;
});

// 3

也可以不传参数调用 first() 来获取集合的第一个元素。如果集合为空,返回 null:

collect([1, 2, 3, 4])->first();

// 1

firstOrFail()

firstOrFail() 方法与 first() 相同,但如果没找到结果,会抛出 Illuminate\Support\ItemNotFoundException 异常:

collect([1, 2, 3, 4])->firstOrFail(function (int $value, int $key) {
    return $value > 5;
});

// 抛出 ItemNotFoundException...

也可以不传参数调用 firstOrFail() 来获取集合的第一个元素。如果集合为空,会抛出 Illuminate\Support\ItemNotFoundException 异常:

collect([])->firstOrFail();

// 抛出 ItemNotFoundException...

firstWhere()

firstWhere() 方法返回集合中第一个匹配给定键值对的元素:

$collection = collect([
    ['name' => 'Regena', 'age' => null],
    ['name' => 'Linda', 'age' => 14],
    ['name' => 'Diego', 'age' => 23],
    ['name' => 'Linda', 'age' => 84],
]);

$collection->firstWhere('name', 'Linda');

// ['name' => 'Linda', 'age' => 14]

也可以传入比较运算符:

$collection->firstWhere('age', '>=', 18);

// ['name' => 'Diego', 'age' => 23]

和 where 方法一样,你也可以只传一个参数。此时 firstWhere() 会返回给定键的值为「真」(truthy)的第一个元素:

$collection->firstWhere('age');

// ['name' => 'Linda', 'age' => 14]

flatMap()

flatMap() 方法遍历集合,将每个值传给给定闭包。闭包可以修改元素并返回,从而形成一个新的修改后元素集合。然后,数组会被展平一层:

$collection = collect([
    ['name' => 'Sally'],
    ['school' => 'Arkansas'],
    ['age' => 28]
]);

$flattened = $collection->flatMap(function (array $values) {
    return array_map('strtoupper', $values);
});

$flattened->all();

// ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => '28'];

flatten()

flatten() 方法将多维集合展平成一维:

$collection = collect([
    'name' => 'Taylor',
    'languages' => [
        'PHP', 'JavaScript'
    ]
]);

$flattened = $collection->flatten();

$flattened->all();

// ['Taylor', 'PHP', 'JavaScript'];

如果有需要,可以给 flatten() 传入一个「深度」参数:

$collection = collect([
    'Apple' => [
        [
            'name' => 'iPhone 6S',
            'brand' => 'Apple'
        ],
    ],
    'Samsung' => [
        [
            'name' => 'Galaxy S7',
            'brand' => 'Samsung'
        ],
    ],
]);

$products = $collection->flatten(1);

$products->values()->all();

/*
    [
        ['name' => 'iPhone 6S', 'brand' => 'Apple'],
        ['name' => 'Galaxy S7', 'brand' => 'Samsung'],
    ]
*/

在这个例子中,如果不传深度参数直接调用 flatten(),嵌套数组也会被展平,结果是 ['iPhone 6S', 'Apple', 'Galaxy S7', 'Samsung']。传入深度可以指定嵌套数组展平的层数。

flip()

flip() 方法将集合的键和值互换:

$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

$flipped = $collection->flip();

$flipped->all();

// ['Taylor' => 'name', 'Laravel' => 'framework']

forget()

forget() 方法通过键从集合中移除元素:

$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

// 移除单个键...
$collection->forget('name');

// ['framework' => 'Laravel']

// 移除多个键...
$collection->forget(['name', 'framework']);

// []

注意

与大多数其他集合方法不同,forget() 不会返回一个新的修改后的集合,而是直接修改并返回调用它的集合本身。

forPage()

forPage() 方法返回一个包含给定页码上元素的新集合。第一个参数是页码,第二个参数是每页显示的元素数:

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

$chunk = $collection->forPage(2, 3);

$chunk->all();

// [4, 5, 6]

fromJson()

静态方法 fromJson() 通过 PHP 的 json_decode 函数解码给定 JSON 字符串来创建新集合实例:

use Illuminate\Support\Collection;

$json = json_encode([
    'name' => 'Taylor Otwell',
    'role' => 'Developer',
    'status' => 'Active',
]);

$collection = Collection::fromJson($json);

get()

get() 方法返回给定键的元素。如果键不存在,返回 null:

$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

$value = $collection->get('name');

// Taylor

可以传入第二个参数作为默认值:

$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']);

$value = $collection->get('age', 34);

// 34

甚至可以传入闭包作为默认值。如果指定键不存在,会执行闭包并返回结果:

$collection->get('email', function () {
    return 'taylor@example.com';
});

// taylor@example.com

groupBy()

groupBy() 方法按给定键对集合元素进行分组:

$collection = collect([
    ['account_id' => 'account-x10', 'product' => 'Chair'],
    ['account_id' => 'account-x10', 'product' => 'Bookcase'],
    ['account_id' => 'account-x11', 'product' => 'Desk'],
]);

$grouped = $collection->groupBy('account_id');

$grouped->all();

/*
    [
        'account-x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
        ],
        'account-x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
        ],
    ]
*/

除了传入字符串键,也可以传入闭包。闭包应返回你想用作分组键的值:

$grouped = $collection->groupBy(function (array $item, int $key) {
    return substr($item['account_id'], -3);
});

$grouped->all();

/*
    [
        'x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
        ],
        'x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
        ],
    ]
*/

可以传入多个分组条件(数组形式)。每个数组元素会应用到多维数组中对应的层级:

$data = new Collection([
    10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
    20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
    30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
    40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
]);

$result = $data->groupBy(['skill', function (array $item) {
    return $item['roles'];
}], preserveKeys: true);

/*
[
    1 => [
        'Role_1' => [
            10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
            20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
        ],
        'Role_2' => [
            20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
        ],
        'Role_3' => [
            10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
        ],
    ],
    2 => [
        'Role_1' => [
            30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
        ],
        'Role_2' => [
            40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
        ],
    ],
];
*/

has()

has() 方法判断集合中是否存在给定的键:

$collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]);

$collection->has('product');

// true

$collection->has(['product', 'amount']);

// true

$collection->has(['amount', 'price']);

// false

hasAny()

hasAny() 方法判断集合中是否存在给定键中的任意一个:

$collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]);

$collection->hasAny(['product', 'price']);

// true

$collection->hasAny(['name', 'price']);

// false

hasMany()

hasMany() 方法判断集合是否包含多个元素:

collect([])->hasMany();

// false

collect(['1'])->hasMany();

// false

collect([1, 2, 3])->hasMany();

// true

collect([
    ['age' => 2],
    ['age' => 3],
])->hasMany(fn ($item) => $item['age'] === 2)

// false

hasSole()

hasSole() 方法判断集合是否只包含一个元素,可选地匹配给定条件:

collect([])->hasSole();

// false

collect(['1'])->hasSole();

// true

collect([1, 2, 3])->hasSole(fn (int $item) => $item === 2);

// true

implode()

implode() 方法将集合中的元素连接成字符串。参数取决于集合元素的类型。如果集合包含数组或对象,应传入你想连接的属性键和放在值之间的「胶水」字符串:

$collection = collect([
    ['account_id' => 1, 'product' => 'Desk'],
    ['account_id' => 2, 'product' => 'Chair'],
]);

$collection->implode('product', ', ');

// 'Desk, Chair'

如果集合包含简单字符串或数字,只需传入「胶水」作为唯一参数:

collect([1, 2, 3, 4, 5])->implode('-');

// '1-2-3-4-5'

如果你想对连接的值进行格式化,可以传入闭包:

$collection->implode(function (array $item, int $key) {
    return strtoupper($item['product']);
}, ', ');

// 'DESK, CHAIR'

intersect()

intersect() 方法从原集合中移除不在给定数组或集合中的值。结果集合会保留原集合的键:

$collection = collect(['Desk', 'Sofa', 'Chair']);

$intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']);

$intersect->all();

// [0 => 'Desk', 2 => 'Chair']

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

intersectUsing()

intersectUsing() 方法与 intersect 类似,但使用自定义回调来比较值。结果集合会保留原集合的键:

$collection = collect(['Desk', 'Sofa', 'Chair']);

$intersect = $collection->intersectUsing(['desk', 'chair', 'bookcase'], function (string $a, string $b) {
    return strcasecmp($a, $b);
});

$intersect->all();

// [0 => 'Desk', 2 => 'Chair']

intersectAssoc()

intersectAssoc() 方法将原集合与另一个集合或数组比较,返回所有给定集合中都存在的键值对:

$collection = collect([
    'color' => 'red',
    'size' => 'M',
    'material' => 'cotton'
]);

$intersect = $collection->intersectAssoc([
    'color' => 'blue',
    'size' => 'M',
    'material' => 'polyester'
]);

$intersect->all();

// ['size' => 'M']

intersectAssocUsing()

intersectAssocUsing() 方法与 intersectAssoc 类似,但使用自定义比较回调来判断键和值的相等性:

$collection = collect([
    'color' => 'red',
    'Size' => 'M',
    'material' => 'cotton',
]);

$intersect = $collection->intersectAssocUsing([
    'color' => 'blue',
    'size' => 'M',
    'material' => 'polyester',
], function (string $a, string $b) {
    return strcasecmp($a, $b);
});

$intersect->all();

// ['Size' => 'M']

intersectByKeys()

intersectByKeys() 方法从原集合中移除不在给定数组或集合中的键及其对应的值:

$collection = collect([
    'serial' => 'UX301', 'type' => 'screen', 'year' => 2009,
]);

$intersect = $collection->intersectByKeys([
    'reference' => 'UX404', 'type' => 'tab', 'year' => 2011,
]);

$intersect->all();

// ['type' => 'screen', 'year' => 2009]

isEmpty()

isEmpty() 方法在集合为空时返回 true,否则返回 false:

collect([])->isEmpty();

// true

isNotEmpty()

isNotEmpty() 方法在集合不为空时返回 true,否则返回 false:

collect([])->isNotEmpty();

// false

join()

join() 方法用字符串连接集合的值。通过第二个参数,你可以指定最后一个元素的连接方式:

collect(['a', 'b', 'c'])->join(', '); // 'a, b, c'
collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c'
collect(['a', 'b'])->join(', ', ' and '); // 'a and b'
collect(['a'])->join(', ', ' and '); // 'a'
collect([])->join(', ', ' and '); // ''

keyBy()

keyBy() 方法按给定键对集合进行键名设置。如果多个元素有相同的键,只有最后一个会出现在新集合中:

$collection = collect([
    ['product_id' => 'prod-100', 'name' => 'Desk'],
    ['product_id' => 'prod-200', 'name' => 'Chair'],
]);

$keyed = $collection->keyBy('product_id');

$keyed->all();

/*
    [
        'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
        'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
    ]
*/

也可以传入闭包。闭包应返回用作键名的值:

$keyed = $collection->keyBy(function (array $item, int $key) {
    return strtoupper($item['product_id']);
});

$keyed->all();

/*
    [
        'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
        'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
    ]
*/

keys()

keys() 方法返回集合的所有键:

$collection = collect([
    'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
    'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
]);

$keys = $collection->keys();

$keys->all();

// ['prod-100', 'prod-200']

last()

last() 方法返回集合中最后一个满足给定条件的元素:

collect([1, 2, 3, 4])->last(function (int $value, int $key) {
    return $value < 3;
});

// 2

也可以不传参数调用 last() 来获取集合的最后一个元素。如果集合为空,返回 null:

collect([1, 2, 3, 4])->last();

// 4

lazy()

lazy() 方法从底层数组返回一个新的 LazyCollection 实例:

$lazyCollection = collect([1, 2, 3, 4])->lazy();

$lazyCollection::class;

// Illuminate\Support\LazyCollection

$lazyCollection->all();

// [1, 2, 3, 4]

当你需要对包含大量元素的巨大 Collection 进行变换时,这个方法特别有用:

$count = $hugeCollection
    ->lazy()
    ->where('country', 'FR')
    ->where('balance', '>', '100')
    ->count();

将集合转换为 LazyCollection 后,可以避免分配大量额外内存。虽然原始集合仍然把它的值保存在内存中,但后续的过滤操作不会。因此,过滤集合结果时几乎不会分配额外内存。

macro()

静态方法 macro() 允许你在运行时给 Collection 类添加方法。更多信息请参考扩展集合文档。

make()

静态方法 make() 创建一个新的集合实例。参见创建集合部分。

use Illuminate\Support\Collection;

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

map()

map() 方法遍历集合,将每个值传给给定回调。回调可以修改元素并返回,从而形成一个新的修改后元素集合:

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

$multiplied = $collection->map(function (int $item, int $key) {
    return $item * 2;
});

$multiplied->all();

// [2, 4, 6, 8, 10]

注意

和大多数其他集合方法一样,map() 返回一个新的集合实例,不会修改调用它的集合。如果你想直接修改原集合,请使用 transform 方法。

mapInto()

mapInto() 方法遍历集合,将每个值传入给定类的构造函数来创建新实例:

class Currency
{
    /**
     * 创建新的 Currency 实例。
     */
    function __construct(
        public string $code,
    ) {}
}

$collection = collect(['USD', 'EUR', 'GBP']);

$currencies = $collection->mapInto(Currency::class);

$currencies->all();

// [Currency('USD'), Currency('EUR'), Currency('GBP')]

mapSpread()

mapSpread() 方法遍历集合中的元素,将每个嵌套元素的值展开传给给定闭包。闭包可以修改元素并返回,从而形成一个新的修改后元素集合:

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

$chunks = $collection->chunk(2);

$sequence = $chunks->mapSpread(function (int $even, int $odd) {
    return $even + $odd;
});

$sequence->all();

// [1, 5, 9, 13, 17]

mapToGroups()

mapToGroups() 方法按给定闭包对集合元素进行分组。闭包应返回一个包含单个键值对的关联数组,从而形成一个新的分组值集合:

$collection = collect([
    [
        'name' => 'John Doe',
        'department' => 'Sales',
    ],
    [
        'name' => 'Jane Doe',
        'department' => 'Sales',
    ],
    [
        'name' => 'Johnny Doe',
        'department' => 'Marketing',
    ]
]);

$grouped = $collection->mapToGroups(function (array $item, int $key) {
    return [$item['department'] => $item['name']];
});

$grouped->all();

/*
    [
        'Sales' => ['John Doe', 'Jane Doe'],
        'Marketing' => ['Johnny Doe'],
    ]
*/

$grouped->get('Sales')->all();

// ['John Doe', 'Jane Doe']

mapWithKeys()

mapWithKeys() 方法遍历集合,将每个值传给给定回调。回调应返回一个包含单个键值对的关联数组:

$collection = collect([
    [
        'name' => 'John',
        'department' => 'Sales',
        'email' => 'john@example.com',
    ],
    [
        'name' => 'Jane',
        'department' => 'Marketing',
        'email' => 'jane@example.com',
    ]
]);

$keyed = $collection->mapWithKeys(function (array $item, int $key) {
    return [$item['email'] => $item['name']];
});

$keyed->all();

/*
    [
        'john@example.com' => 'John',
        'jane@example.com' => 'Jane',
    ]
*/

max()

max() 方法返回给定键的最大值:

$max = collect([
    ['foo' => 10],
    ['foo' => 20]
])->max('foo');

// 20

$max = collect([1, 2, 3, 4, 5])->max();

// 5

median()

median() 方法返回给定键的中位数:

$median = collect([
    ['foo' => 10],
    ['foo' => 10],
    ['foo' => 20],
    ['foo' => 40]
])->median('foo');

// 15

$median = collect([1, 1, 2, 4])->median();

// 1.5

merge()

merge() 方法将给定数组或集合与原集合合并。如果给定项的字符串键与原集合的字符串键匹配,给定项的值会覆盖原集合中的值:

$collection = collect(['product_id' => 1, 'price' => 100]);

$merged = $collection->merge(['price' => 200, 'discount' => false]);

$merged->all();

// ['product_id' => 1, 'price' => 200, 'discount' => false]

如果给定项的键是数字,值会被追加到集合末尾:

$collection = collect(['Desk', 'Chair']);

$merged = $collection->merge(['Bookcase', 'Door']);

$merged->all();

// ['Desk', 'Chair', 'Bookcase', 'Door']

mergeRecursive()

mergeRecursive() 方法将给定数组或集合与原集合递归合并。如果给定项的字符串键与原集合的字符串键匹配,这些键的值会合并成一个数组,且递归处理:

$collection = collect(['product_id' => 1, 'price' => 100]);

$merged = $collection->mergeRecursive([
    'product_id' => 2,
    'price' => 200,
    'discount' => false
]);

$merged->all();

// ['product_id' => [1, 2], 'price' => [100, 200], 'discount' => false]

min()

min() 方法返回给定键的最小值:

$min = collect([
    ['foo' => 10],
    ['foo' => 20]
])->min('foo');

// 10

$min = collect([1, 2, 3, 4, 5])->min();

// 1

mode()

mode() 方法返回给定键的众数:

$mode = collect([
    ['foo' => 10],
    ['foo' => 10],
    ['foo' => 20],
    ['foo' => 40]
])->mode('foo');

// [10]

$mode = collect([1, 1, 2, 4])->mode();

// [1]

$mode = collect([1, 1, 2, 2])->mode();

// [1, 2]

multiply()

multiply() 方法创建集合中所有元素的指定份数副本:

$users = collect([
    ['name' => 'User #1', 'email' => 'user1@example.com'],
    ['name' => 'User #2', 'email' => 'user2@example.com'],
])->multiply(3);

/*
    [
        ['name' => 'User #1', 'email' => 'user1@example.com'],
        ['name' => 'User #2', 'email' => 'user2@example.com'],
        ['name' => 'User #1', 'email' => 'user1@example.com'],
        ['name' => 'User #2', 'email' => 'user2@example.com'],
        ['name' => 'User #1', 'email' => 'user1@example.com'],
        ['name' => 'User #2', 'email' => 'user2@example.com'],
    ]
*/

nth()

nth() 方法创建一个由每第 n 个元素组成的新集合:

$collection = collect(['a', 'b', 'c', 'd', 'e', 'f']);

$collection->nth(4);

// ['a', 'e']

可以传入第二个参数作为起始偏移量:

$collection->nth(4, 1);

// ['b', 'f']

only()

only() 方法返回集合中指定键的元素:

$collection = collect([
    'product_id' => 1,
    'name' => 'Desk',
    'price' => 100,
    'discount' => false
]);

$filtered = $collection->only(['product_id', 'name']);

$filtered->all();

// ['product_id' => 1, 'name' => 'Desk']

only() 的反向方法参见 except。

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

pad()

pad() 方法会用给定值填充数组,直到达到指定大小。它的行为类似 PHP 的 array_pad 函数。

要向左侧填充,应指定负的大小。如果给定大小的绝对值小于或等于数组长度,则不会填充:

$collection = collect(['A', 'B', 'C']);

$filtered = $collection->pad(5, 0);

$filtered->all();

// ['A', 'B', 'C', 0, 0]

$filtered = $collection->pad(-5, 0);

$filtered->all();

// [0, 0, 'A', 'B', 'C']

partition()

partition() 方法可以结合 PHP 数组解构,将满足条件的元素和不满足条件的元素分开:

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

[$underThree, $equalOrAboveThree] = $collection->partition(function (int $i) {
    return $i < 3;
});

$underThree->all();

// [1, 2]

$equalOrAboveThree->all();

// [3, 4, 5, 6]

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

percentage()

percentage() 方法可用于快速计算集合中满足给定条件的元素所占百分比:

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

$percentage = $collection->percentage(fn (int $value) => $value === 1);

// 33.33

默认情况下,百分比会四舍五入到两位小数。你也可以通过第二个参数自定义:

$percentage = $collection->percentage(fn (int $value) => $value === 1, precision: 3);

// 33.333

pipe()

pipe() 方法将集合传给给定闭包,并返回闭包执行的结果:

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

$piped = $collection->pipe(function (Collection $collection) {
    return $collection->sum();
});

// 6

pipeInto()

pipeInto() 方法创建给定类的新实例,并将集合传入构造函数:

class ResourceCollection
{
    /**
     * 创建新的 ResourceCollection 实例。
     */
    public function __construct(
        public Collection $collection,
    ) {}
}

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

$resource = $collection->pipeInto(ResourceCollection::class);

$resource->collection->all();

// [1, 2, 3]

pipeThrough()

pipeThrough() 方法将集合传给一个闭包数组,并返回闭包执行的结果:

use Illuminate\Support\Collection;

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

$result = $collection->pipeThrough([
    function (Collection $collection) {
        return $collection->merge([4, 5]);
    },
    function (Collection $collection) {
        return $collection->sum();
    },
]);

// 15

pluck()

pluck() 方法检索给定键的所有值:

$collection = collect([
    ['product_id' => 'prod-100', 'name' => 'Desk'],
    ['product_id' => 'prod-200', 'name' => 'Chair'],
]);

$plucked = $collection->pluck('name');

$plucked->all();

// ['Desk', 'Chair']

你也可以指定结果集合的键名:

$plucked = $collection->pluck('name', 'product_id');

$plucked->all();

// ['prod-100' => 'Desk', 'prod-200' => 'Chair']

pluck() 方法还支持用「点」记法检索嵌套值:

$collection = collect([
    [
        'name' => 'Laracon',
        'speakers' => [
            'first_day' => ['Rosa', 'Judith'],
        ],
    ],
    [
        'name' => 'VueConf',
        'speakers' => [
            'first_day' => ['Abigail', 'Joey'],
        ],
    ],
]);

$plucked = $collection->pluck('speakers.first_day');

$plucked->all();

// [['Rosa', 'Judith'], ['Abigail', 'Joey']]

如果存在重复键,最后一个匹配的元素会被插入到结果集合中:

$collection = collect([
    ['brand' => 'Tesla',  'color' => 'red'],
    ['brand' => 'Pagani', 'color' => 'white'],
    ['brand' => 'Tesla',  'color' => 'black'],
    ['brand' => 'Pagani', 'color' => 'orange'],
]);

$plucked = $collection->pluck('color', 'brand');

$plucked->all();

// ['Tesla' => 'black', 'Pagani' => 'orange']

pop()

pop() 方法移除并返回集合的最后一个元素。如果集合为空,返回 null:

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

$collection->pop();

// 5

$collection->all();

// [1, 2, 3, 4]

可以传入整数来移除并返回末尾的多个元素:

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

$collection->pop(3);

// collect([5, 4, 3])

$collection->all();

// [1, 2]

prepend()

prepend() 方法将元素添加到集合的开头:

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

$collection->prepend(0);

$collection->all();

// [0, 1, 2, 3, 4, 5]

也可以传入第二个参数来指定添加元素的键:

$collection = collect(['one' => 1, 'two' => 2]);

$collection->prepend(0, 'zero');

$collection->all();

// ['zero' => 0, 'one' => 1, 'two' => 2]

pull()

pull() 方法通过键从集合中移除并返回元素:

$collection = collect(['product_id' => 'prod-100', 'name' => 'Desk']);

$collection->pull('name');

// 'Desk'

$collection->all();

// ['product_id' => 'prod-100']

push()

push() 方法将元素追加到集合末尾:

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

$collection->push(5);

$collection->all();

// [1, 2, 3, 4, 5]

也可以一次追加多个元素:

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

$collection->push(5, 6, 7);

$collection->all();

// [1, 2, 3, 4, 5, 6, 7]

put()

put() 方法在集合中设置给定的键和值:

$collection = collect(['product_id' => 1, 'name' => 'Desk']);

$collection->put('price', 100);

$collection->all();

// ['product_id' => 1, 'name' => 'Desk', 'price' => 100]

random()

random() 方法从集合中返回一个随机元素:

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

$collection->random();

// 4 - (随机获取)

可以传入整数指定要随机获取的元素数量。显式传入数量时,始终返回一个集合:

$random = $collection->random(3);

$random->all();

// [2, 4, 5] - (随机获取)

如果集合的元素少于请求数量,random() 方法会抛出 InvalidArgumentException。

random() 方法还接受闭包,闭包会接收当前集合实例:

use Illuminate\Support\Collection;

$random = $collection->random(fn (Collection $items) => min(10, count($items)));

$random->all();

// [1, 2, 3, 4, 5] - (随机获取)

range()

range() 方法返回一个包含指定范围内整数的集合:

$collection = collect()->range(3, 6);

$collection->all();

// [3, 4, 5, 6]

reduce()

reduce() 方法将集合缩减为单个值,每次迭代的结果会传给下一次迭代:

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

$total = $collection->reduce(function (?int $carry, int $item) {
    return $carry + $item;
});

// 6

第一次迭代时 $carry 的值为 null;你可以通过第二个参数指定初始值:

$collection->reduce(function (int $carry, int $item) {
    return $carry + $item;
}, 4);

// 10

reduce() 方法也会将数组键传给回调:

$collection = collect([
    'usd' => 1400,
    'gbp' => 1200,
    'eur' => 1000,
]);

$ratio = [
    'usd' => 1,
    'gbp' => 1.37,
    'eur' => 1.22,
];

$collection->reduce(function (int $carry, int $value, string $key) use ($ratio) {
    return $carry + ($value * $ratio[$key]);
}, 0);

// 4264

reduceInto()

reduceInto() 方法通过修改给定的初始值来将集合缩减为单个值。与 reduce 方法不同,给定回调不需要返回累积值:

class OrderStats
{
    public int $total = 0;

    public int $count = 0;
}

$orders = collect([
    ['amount' => 100],
    ['amount' => 250],
    ['amount' => 50],
]);

$stats = $orders->reduceInto(new OrderStats, function (OrderStats $stats, array $order) {
    $stats->total += $order['amount'];
    $stats->count++;
});

$stats->total;

// 400

当缩减为标量或数组时,应在回调中按引用接收,这样修改才会应用到原始值:

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

$even = $collection->reduceInto([], function (array &$result, int $value) {
    if ($value % 2 === 0) {
        $result[] = $value;
    }
});

// [2, 4]

reduceSpread()

reduceSpread() 方法将集合缩减为一个值数组,每次迭代的结果传给下一次迭代。此方法类似 reduce,但可以接受多个初始值:

[$creditsRemaining, $batch] = Image::where('status', 'unprocessed')
    ->get()
    ->reduceSpread(function (int $creditsRemaining, Collection $batch, Image $image) {
        if ($creditsRemaining >= $image->creditsRequired()) {
            $batch->push($image);

            $creditsRemaining -= $image->creditsRequired();
        }

        return [$creditsRemaining, $batch];
    }, $creditsAvailable, collect());

reject()

reject() 方法用给定闭包过滤集合。闭包返回 true 时,该元素会从结果集合中移除:

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

$filtered = $collection->reject(function (int $value, int $key) {
    return $value > 2;
});

$filtered->all();

// [1, 2]

reject() 的反向方法参见 filter。

replace()

replace() 方法的行为类似 merge;但除了覆盖匹配字符串键的元素外,replace 方法还会覆盖匹配数字键的元素:

$collection = collect(['Taylor', 'Abigail', 'James']);

$replaced = $collection->replace([1 => 'Victoria', 3 => 'Finn']);

$replaced->all();

// ['Taylor', 'Victoria', 'James', 'Finn']

replaceRecursive()

replaceRecursive() 方法的行为类似 replace,但它会递归进入数组,对内部值应用相同的替换过程:

$collection = collect([
    'Taylor',
    'Abigail',
    [
        'James',
        'Victoria',
        'Finn'
    ]
]);

$replaced = $collection->replaceRecursive([
    'Charlie',
    2 => [1 => 'King']
]);

$replaced->all();

// ['Charlie', 'Abigail', ['James', 'King', 'Finn']]

reverse()

reverse() 方法反转集合元素的顺序,保留原始键:

$collection = collect(['a', 'b', 'c', 'd', 'e']);

$reversed = $collection->reverse();

$reversed->all();

/*
    [
        4 => 'e',
        3 => 'd',
        2 => 'c',
        1 => 'b',
        0 => 'a',
    ]
*/

search() 方法在集合中搜索给定值,找到则返回其键。如果找不到,返回 false:

$collection = collect([2, 4, 6, 8]);

$collection->search(4);

// 1

搜索使用「宽松比较」,即包含整数值的字符串会和相同值的整数被视为相等。要使用「严格比较」,传入 true 作为第二个参数:

collect([2, 4, 6, 8])->search('4', strict: true);

// false

也可以传入闭包来查找第一个满足条件的元素:

collect([2, 4, 6, 8])->search(function (int $item, int $key) {
    return $item > 5;
});

// 2

select()

select() 方法从集合中选取给定的键,类似 SQL 的 SELECT 语句:

$users = collect([
    ['name' => 'Taylor Otwell', 'role' => 'Developer', 'status' => 'active'],
    ['name' => 'Victoria Faith', 'role' => 'Researcher', 'status' => 'active'],
]);

$users->select(['name', 'role']);

/*
    [
        ['name' => 'Taylor Otwell', 'role' => 'Developer'],
        ['name' => 'Victoria Faith', 'role' => 'Researcher'],
    ],
*/

shift()

shift() 方法移除并返回集合的第一个元素:

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

$collection->shift();

// 1

$collection->all();

// [2, 3, 4, 5]

可以传入整数来移除并返回开头的多个元素:

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

$collection->shift(3);

// collect([1, 2, 3])

$collection->all();

// [4, 5]

shuffle()

shuffle() 方法随机打乱集合中的元素:

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

$shuffled = $collection->shuffle();

$shuffled->all();

// [3, 2, 5, 1, 4] - (随机生成)

skip()

skip() 方法返回一个新集合,从开头移除指定数量的元素:

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

$collection = $collection->skip(4);

$collection->all();

// [5, 6, 7, 8, 9, 10]

skipUntil()

skipUntil() 方法跳过元素直到给定回调返回 true。回调返回 true 后,集合中剩余的元素会作为新集合返回:

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

$subset = $collection->skipUntil(function (int $item) {
    return $item >= 3;
});

$subset->all();

// [3, 4]

也可以传入一个值,跳过元素直到找到该值:

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

$subset = $collection->skipUntil(3);

$subset->all();

// [3, 4]

注意

如果给定的值不存在或回调始终不返回 true,skipUntil() 方法会返回空集合。

skipWhile()

skipWhile() 方法跳过元素直到给定回调返回 false。回调返回 false 后,集合中剩余的元素会作为新集合返回:

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

$subset = $collection->skipWhile(function (int $item) {
    return $item <= 3;
});

$subset->all();

// [4]

注意

如果回调始终不返回 false,skipWhile() 方法会返回空集合。

slice()

slice() 方法返回从给定索引开始的集合切片:

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

$slice = $collection->slice(4);

$slice->all();

// [5, 6, 7, 8, 9, 10]

如果想限制切片大小,传入第二个参数:

$slice = $collection->slice(4, 2);

$slice->all();

// [5, 6]

返回的切片默认保留键。如果不希望保留原始键,可以使用 values 方法重新索引。

sliding()

sliding() 方法返回一个由「滑动窗口」视图的块组成的新集合:

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

$chunks = $collection->sliding(2);

$chunks->toArray();

// [[1, 2], [2, 3], [3, 4], [4, 5]]

这个方法与 eachSpread 配合使用特别方便:

$transactions->sliding(2)->eachSpread(function (Collection $previous, Collection $current) {
    $current->total = $previous->total + $current->amount;
});

可以传入第二个「步长」值,决定每个块首项之间的距离:

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

$chunks = $collection->sliding(3, step: 2);

$chunks->toArray();

// [[1, 2, 3], [3, 4, 5]]

sole()

sole() 方法返回集合中第一个满足给定条件的元素,但前提是只有一个元素满足条件:

collect([1, 2, 3, 4])->sole(function (int $value, int $key) {
    return $value === 2;
});

// 2

也可以传入键值对,返回第一个匹配的元素,但前提是只有一个元素匹配:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
]);

$collection->sole('product', 'Chair');

// ['product' => 'Chair', 'price' => 100]

还可以不传参数调用 sole(),在集合只有一个元素时获取该元素:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
]);

$collection->sole();

// ['product' => 'Desk', 'price' => 200]

如果集合中没有被 sole() 返回的元素,会抛出 \Illuminate\Collections\ItemNotFoundException 异常。如果有多个元素应该被返回,会抛出 \Illuminate\Collections\MultipleItemsFoundException 异常。

some()

contains 方法的别名。

sort()

sort() 方法对集合排序。排序后的集合保留原始数组键,所以在下面的例子中我们用 values 方法将键重置为连续数字索引:

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

$sorted = $collection->sort();

$sorted->values()->all();

// [1, 2, 3, 4, 5]

如果你的排序需求更高级,可以传入带自定义算法的回调。请参考 PHP 文档中的 uasort,这是集合的 sort() 方法内部调用的 PHP 函数。

信息

如果需要对嵌套数组或对象的集合排序,请参考 sortBy 和 sortByDesc 方法。

sortBy()

sortBy() 方法按给定键对集合排序。排序后的集合保留原始数组键,所以在下面的例子中我们用 values 方法将键重置为连续数字索引:

$collection = collect([
    ['name' => 'Desk', 'price' => 200],
    ['name' => 'Chair', 'price' => 100],
    ['name' => 'Bookcase', 'price' => 150],
]);

$sorted = $collection->sortBy('price');

$sorted->values()->all();

/*
    [
        ['name' => 'Chair', 'price' => 100],
        ['name' => 'Bookcase', 'price' => 150],
        ['name' => 'Desk', 'price' => 200],
    ]
*/

sortBy() 方法接受排序标志作为第二个参数:

$collection = collect([
    ['title' => 'Item 1'],
    ['title' => 'Item 12'],
    ['title' => 'Item 3'],
]);

$sorted = $collection->sortBy('title', SORT_NATURAL);

$sorted->values()->all();

/*
    [
        ['title' => 'Item 1'],
        ['title' => 'Item 3'],
        ['title' => 'Item 12'],
    ]
*/

也可以传入闭包来决定如何排序:

$collection = collect([
    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
    ['name' => 'Chair', 'colors' => ['Black']],
    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]);

$sorted = $collection->sortBy(function (array $product, int $key) {
    return count($product['colors']);
});

$sorted->values()->all();

/*
    [
        ['name' => 'Chair', 'colors' => ['Black']],
        ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
        ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
    ]
*/

如果想按多个属性排序,可以传入排序操作数组。每个排序操作是一个包含排序属性和方向的数组:

$collection = collect([
    ['name' => 'Taylor Otwell', 'age' => 34],
    ['name' => 'Abigail Otwell', 'age' => 30],
    ['name' => 'Taylor Otwell', 'age' => 36],
    ['name' => 'Abigail Otwell', 'age' => 32],
]);

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

$sorted->values()->all();

/*
    [
        ['name' => 'Abigail Otwell', 'age' => 32],
        ['name' => 'Abigail Otwell', 'age' => 30],
        ['name' => 'Taylor Otwell', 'age' => 36],
        ['name' => 'Taylor Otwell', 'age' => 34],
    ]
*/

按多个属性排序时,也可以传入定义每个排序操作的闭包:

$collection = collect([
    ['name' => 'Taylor Otwell', 'age' => 34],
    ['name' => 'Abigail Otwell', 'age' => 30],
    ['name' => 'Taylor Otwell', 'age' => 36],
    ['name' => 'Abigail Otwell', 'age' => 32],
]);

$sorted = $collection->sortBy([
    fn (array $a, array $b) => $a['name'] <=> $b['name'],
    fn (array $a, array $b) => $b['age'] <=> $a['age'],
]);

$sorted->values()->all();

/*
    [
        ['name' => 'Abigail Otwell', 'age' => 32],
        ['name' => 'Abigail Otwell', 'age' => 30],
        ['name' => 'Taylor Otwell', 'age' => 36],
        ['name' => 'Taylor Otwell', 'age' => 34],
    ]
*/

sortByDesc()

此方法签名与 sortBy 相同,但按相反方向排序。

sortDesc()

此方法按 sort 相反的方向排序:

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

$sorted = $collection->sortDesc();

$sorted->values()->all();

// [5, 4, 3, 2, 1]

与 sort() 不同,sortDesc() 不能传入闭包。如果需要,请使用 sort 方法并反转你的比较。

sortKeys()

sortKeys() 方法按底层关联数组的键排序:

$collection = collect([
    'id' => 22345,
    'first' => 'John',
    'last' => 'Doe',
]);

$sorted = $collection->sortKeys();

$sorted->all();

/*
    [
        'first' => 'John',
        'id' => 22345,
        'last' => 'Doe',
    ]
*/

sortKeysDesc()

此方法签名与 sortKeys 相同,但按相反方向排序。

sortKeysUsing()

sortKeysUsing() 方法使用回调按底层关联数组的键排序:

$collection = collect([
    'ID' => 22345,
    'first' => 'John',
    'last' => 'Doe',
]);

$sorted = $collection->sortKeysUsing('strnatcasecmp');

$sorted->all();

/*
    [
        'first' => 'John',
        'ID' => 22345,
        'last' => 'Doe',
    ]
*/

回调必须是一个比较函数,返回一个小于、等于或大于零的整数。更多细节请参考 PHP 文档中的 uksort,这是 sortKeysUsing() 方法内部使用的 PHP 函数。

splice()

splice() 方法从指定索引开始移除并返回一个切片:

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

$chunk = $collection->splice(2);

$chunk->all();

// [3, 4, 5]

$collection->all();

// [1, 2]

可以传入第二个参数来限制结果集合的大小:

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

$chunk = $collection->splice(2, 1);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 4, 5]

此外,可以传入第三个参数,包含要替换被移除元素的新元素:

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

$chunk = $collection->splice(2, 1, [10, 11]);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 10, 11, 4, 5]

split()

split() 方法将集合分成给定数量的组:

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

$groups = $collection->split(3);

$groups->all();

// [[1, 2], [3, 4], [5]]

splitIn()

splitIn() 方法将集合分成给定数量的组,先填满非末尾组,再将剩余元素分配到最后一组:

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

$groups = $collection->splitIn(3);

$groups->all();

// [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

sum()

sum() 方法返回集合中所有元素的总和:

collect([1, 2, 3, 4, 5])->sum();

// 15

如果集合包含嵌套数组或对象,应传入用于确定求和值的键:

$collection = collect([
    ['name' => 'JavaScript: The Good Parts', 'pages' => 176],
    ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],
]);

$collection->sum('pages');

// 1272

也可以传入闭包来决定求和哪些值:

$collection = collect([
    ['name' => 'Chair', 'colors' => ['Black']],
    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
]);

$collection->sum(function (array $product) {
    return count($product['colors']);
});

// 6

take()

take() 方法返回指定数量的元素组成的新集合:

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

$chunk = $collection->take(3);

$chunk->all();

// [0, 1, 2]

也可以传入负整数来从末尾获取指定数量的元素:

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

$chunk = $collection->take(-2);

$chunk->all();

// [4, 5]

takeUntil()

takeUntil() 方法返回集合中的元素,直到给定回调返回 true:

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

$subset = $collection->takeUntil(function (int $item) {
    return $item >= 3;
});

$subset->all();

// [1, 2]

也可以传入一个值,获取元素直到找到该值:

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

$subset = $collection->takeUntil(3);

$subset->all();

// [1, 2]

注意

如果给定的值不存在或回调始终不返回 true,takeUntil() 方法会返回集合中的所有元素。

takeWhile()

takeWhile() 方法返回集合中的元素,直到给定回调返回 false:

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

$subset = $collection->takeWhile(function (int $item) {
    return $item < 3;
});

$subset->all();

// [1, 2]

注意

如果回调始终不返回 false,takeWhile() 方法会返回集合中的所有元素。

tap()

tap() 方法将集合传给给定回调,让你在特定点「 tap 」进集合对元素做些操作而不影响集合本身。然后集合由 tap() 方法返回:

collect([2, 4, 3, 1, 5])
    ->sort()
    ->tap(function (Collection $collection) {
        Log::debug('Values after sorting', $collection->values()->all());
    })
    ->shift();

// 1

times()

静态方法 times() 通过调用给定闭包指定次数来创建新集合:

$collection = Collection::times(10, function (int $number) {
    return $number * 9;
});

$collection->all();

// [9, 18, 27, 36, 45, 54, 63, 72, 81, 90]

toArray()

toArray() 方法将集合转换为普通 PHP 数组。如果集合的值是 Eloquent 模型,模型也会被转为数组:

$collection = collect(['name' => 'Desk', 'price' => 200]);

$collection->toArray();

/*
    [
        ['name' => 'Desk', 'price' => 200],
    ]
*/

注意

toArray() 还会将集合中所有实现了 Arrayable 的嵌套对象转为数组。如果你想获取集合底层的原始数组,请使用 all 方法。

toJson()

toJson() 方法将集合转换为 JSON 序列化字符串:

$collection = collect(['name' => 'Desk', 'price' => 200]);

$collection->toJson();

// '{"name":"Desk", "price":200}'

toPrettyJson()

toPrettyJson() 方法使用 JSON_PRETTY_PRINT 选项将集合转换为格式化的 JSON 字符串:

$collection = collect(['name' => 'Desk', 'price' => 200]);

$collection->toPrettyJson();

transform()

transform() 方法遍历集合,对每个元素调用给定回调。集合中的元素会被回调返回的值替换:

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

$collection->transform(function (int $item, int $key) {
    return $item * 2;
});

$collection->all();

// [2, 4, 6, 8, 10]

注意

与大多数其他集合方法不同,transform() 会直接修改集合本身。如果想创建新集合,请使用 map 方法。

undot()

undot() 方法将使用「点」记法的单维集合展开为多维集合:

$person = collect([
    'name.first_name' => 'Marie',
    'name.last_name' => 'Valentine',
    'address.line_1' => '2992 Eagle Drive',
    'address.line_2' => '',
    'address.suburb' => 'Detroit',
    'address.state' => 'MI',
    'address.postcode' => '48219'
]);

$person = $person->undot();

$person->toArray();

/*
    [
        "name" => [
            "first_name" => "Marie",
            "last_name" => "Valentine",
        ],
        "address" => [
            "line_1" => "2992 Eagle Drive",
            "line_2" => "",
            "suburb" => "Detroit",
            "state" => "MI",
            "postcode" => "48219",
        ],
    ]
*/

union()

union() 方法将给定数组添加到集合中。如果给定数组包含原集合已有的键,原集合的值会被保留:

$collection = collect([1 => ['a'], 2 => ['b']]);

$union = $collection->union([3 => ['c'], 1 => ['d']]);

$union->all();

// [1 => ['a'], 2 => ['b'], 3 => ['c']]

unique()

unique() 方法返回集合中所有唯一的元素。返回的集合保留原始数组键,所以在下面的例子中我们用 values 方法将键重置为连续数字索引:

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

$unique = $collection->unique();

$unique->values()->all();

// [1, 2, 3, 4]

处理嵌套数组或对象时,可以指定用于判断唯一性的键:

$collection = collect([
    ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
    ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'],
    ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
    ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
    ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
]);

$unique = $collection->unique('brand');

$unique->values()->all();

/*
    [
        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
    ]
*/

最后,也可以传入闭包来指定用于判断唯一性的值:

$unique = $collection->unique(function (array $item) {
    return $item['brand'].$item['type'];
});

$unique->values()->all();

/*
    [
        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
        ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
    ]
*/

unique() 方法在检查值时使用「宽松比较」。要用「严格比较」,请使用 uniqueStrict 方法。

信息

使用 Eloquent 集合时,此方法的行为会有所不同。

uniqueStrict()

此方法签名与 unique 相同,但所有值比较都使用「严格比较」。

unless()

unless() 方法在第一个参数求值为 false 时执行给定回调。集合实例和第一个参数会传给闭包:

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

$collection->unless(true, function (Collection $collection, bool $value) {
    return $collection->push(4);
});

$collection->unless(false, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 5]

可以传入第二个回调。当第一个参数求值为 true 时,会执行第二个回调:

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

$collection->unless(true, function (Collection $collection, bool $value) {
    return $collection->push(4);
}, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 5]

unless() 的反向方法参见 when。

unlessEmpty()

whenNotEmpty 方法的别名。

unlessNotEmpty()

whenEmpty 方法的别名。

unwrap()

静态方法 unwrap() 在适用时从给定值中返回集合底层的元素:

Collection::unwrap(collect('John Doe'));

// ['John Doe']

Collection::unwrap(['John Doe']);

// ['John Doe']

Collection::unwrap('John Doe');

// 'John Doe'

value()

value() 方法从集合的第一个元素中检索给定值:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Speaker', 'price' => 400],
]);

$value = $collection->value('price');

// 200

values()

values() 方法返回一个键被重置为连续整数的新集合:

$collection = collect([
    10 => ['product' => 'Desk', 'price' => 200],
    11 => ['product' => 'Speaker', 'price' => 400],
]);

$values = $collection->values();

$values->all();

/*
    [
        0 => ['product' => 'Desk', 'price' => 200],
        1 => ['product' => 'Speaker', 'price' => 400],
    ]
*/

when()

when() 方法在第一个参数求值为 true 时执行给定回调。集合实例和第一个参数会传给闭包:

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

$collection->when(true, function (Collection $collection, bool $value) {
    return $collection->push(4);
});

$collection->when(false, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 4]

可以传入第二个回调。当第一个参数求值为 false 时,会执行第二个回调:

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

$collection->when(false, function (Collection $collection, bool $value) {
    return $collection->push(4);
}, function (Collection $collection, bool $value) {
    return $collection->push(5);
});

$collection->all();

// [1, 2, 3, 5]

when() 的反向方法参见 unless。

whenEmpty()

whenEmpty() 方法在集合为空时执行给定回调:

$collection = collect(['Michael', 'Tom']);

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Michael', 'Tom']

$collection = collect();

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Adam']

可以传入第二个闭包,在集合不为空时执行:

$collection = collect(['Michael', 'Tom']);

$collection->whenEmpty(function (Collection $collection) {
    return $collection->push('Adam');
}, function (Collection $collection) {
    return $collection->push('Taylor');
});

$collection->all();

// ['Michael', 'Tom', 'Taylor']

whenEmpty() 的反向方法参见 whenNotEmpty。

whenNotEmpty()

whenNotEmpty() 方法在集合不为空时执行给定回调:

$collection = collect(['Michael', 'Tom']);

$collection->whenNotEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// ['Michael', 'Tom', 'Adam']

$collection = collect();

$collection->whenNotEmpty(function (Collection $collection) {
    return $collection->push('Adam');
});

$collection->all();

// []

可以传入第二个闭包,在集合为空时执行:

$collection = collect();

$collection->whenNotEmpty(function (Collection $collection) {
    return $collection->push('Adam');
}, function (Collection $collection) {
    return $collection->push('Taylor');
});

$collection->all();

// ['Taylor']

whenNotEmpty() 的反向方法参见 whenEmpty。

where()

where() 方法按给定键值对过滤集合:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->where('price', 100);

$filtered->all();

/*
    [
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Door', 'price' => 100],
    ]
*/

where() 方法在检查值时使用「宽松比较」。要用「严格比较」,请使用 whereStrict 方法;要过滤 null 值,请使用 whereNull 和 whereNotNull 方法。

也可以传入比较运算符作为第二个参数。支持的运算符有:===、!==、!=、==、=、<>、>、<、>=、<=:

$collection = collect([
    ['name' => 'Jim', 'platform' => 'Mac'],
    ['name' => 'Sally', 'platform' => 'Mac'],
    ['name' => 'Sue', 'platform' => 'Linux'],
]);

$filtered = $collection->where('platform', '!=', 'Linux');

$filtered->all();

/*
    [
        ['name' => 'Jim', 'platform' => 'Mac'],
        ['name' => 'Sally', 'platform' => 'Mac'],
    ]
*/

whereStrict()

此方法签名与 where 相同,但所有值比较都使用「严格比较」。

whereBetween()

whereBetween() 方法通过判断指定值是否在给定范围内来过滤集合:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 80],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Pencil', 'price' => 30],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->whereBetween('price', [100, 200]);

$filtered->all();

/*
    [
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Door', 'price' => 100],
    ]
*/

whereIn()

whereIn() 方法移除集合中指定值不在给定数组内的元素:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->whereIn('price', [150, 200]);

$filtered->all();

/*
    [
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Bookcase', 'price' => 150],
    ]
*/

whereIn() 方法在检查值时使用「宽松比较」。要用「严格比较」,请使用 whereInStrict 方法。

whereInStrict()

此方法签名与 whereIn 相同,但所有值比较都使用「严格比较」。

whereInstanceOf()

whereInstanceOf() 方法按给定类类型过滤集合:

use App\Models\User;
use App\Models\Post;

$collection = collect([
    new User,
    new User,
    new Post,
]);

$filtered = $collection->whereInstanceOf(User::class);

$filtered->all();

// [App\Models\User, App\Models\User]

whereNotBetween()

whereNotBetween() 方法通过判断指定值是否在给定范围外来过滤集合:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 80],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Pencil', 'price' => 30],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->whereNotBetween('price', [100, 200]);

$filtered->all();

/*
    [
        ['product' => 'Chair', 'price' => 80],
        ['product' => 'Pencil', 'price' => 30],
    ]
*/

whereNotIn()

whereNotIn() 方法移除集合中指定值在给定数组内的元素:

$collection = collect([
    ['product' => 'Desk', 'price' => 200],
    ['product' => 'Chair', 'price' => 100],
    ['product' => 'Bookcase', 'price' => 150],
    ['product' => 'Door', 'price' => 100],
]);

$filtered = $collection->whereNotIn('price', [150, 200]);

$filtered->all();

/*
    [
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Door', 'price' => 100],
    ]
*/

whereNotIn() 方法在检查值时使用「宽松比较」。要用「严格比较」,请使用 whereNotInStrict 方法。

whereNotInStrict()

此方法签名与 whereNotIn 相同,但所有值比较都使用「严格比较」。

whereNotNull()

whereNotNull() 方法返回集合中给定键不为 null 的元素:

$collection = collect([
    ['name' => 'Desk'],
    ['name' => null],
    ['name' => 'Bookcase'],
    ['name' => 0],
    ['name' => ''],
]);

$filtered = $collection->whereNotNull('name');

$filtered->all();

/*
    [
        ['name' => 'Desk'],
        ['name' => 'Bookcase'],
        ['name' => 0],
        ['name' => ''],
    ]
*/

whereNull()

whereNull() 方法返回集合中给定键为 null 的元素:

$collection = collect([
    ['name' => 'Desk'],
    ['name' => null],
    ['name' => 'Bookcase'],
    ['name' => 0],
    ['name' => ''],
]);

$filtered = $collection->whereNull('name');

$filtered->all();

/*
    [
        ['name' => null],
    ]
*/

wrap()

静态方法 wrap() 在适用时将给定值包装成集合:

use Illuminate\Support\Collection;

$collection = Collection::wrap('John Doe');

$collection->all();

// ['John Doe']

$collection = Collection::wrap(['John Doe']);

$collection->all();

// ['John Doe']

$collection = Collection::wrap(collect('John Doe'));

$collection->all();

// ['John Doe']

zip()

zip() 方法将给定数组的值与原集合的值按对应索引合并在一起:

$collection = collect(['Chair', 'Desk']);

$zipped = $collection->zip([100, 200]);

$zipped->all();

// [['Chair', 100], ['Desk', 200]]

高阶消息

集合还支持「高阶消息」(higher order messages),这是对集合执行常见操作的快捷方式。提供高阶消息的集合方法有:average、avg、contains、each、every、filter、first、flatMap、groupBy、keyBy、map、max、min、partition、reject、skipUntil、skipWhile、some、sortBy、sortByDesc、sum、takeUntil、takeWhile 和 unique。

每个高阶消息都可以作为集合实例的动态属性来访问。比如,用 each 高阶消息对集合中每个对象调用方法:

use App\Models\User;

$users = User::where('votes', '>', 500)->get();

$users->each->markAsVip();

同样,用 sum 高阶消息来汇总用户集合的「投票数」:

$users = User::where('group', 'Development')->get();

return $users->sum->votes;

惰性集合

介绍

注意

在深入了解 Laravel 的惰性集合之前,建议先熟悉一下 PHP 生成器。

为了补充已经非常强大的 Collection 类,LazyCollection 类利用 PHP 的生成器来让你在保持低内存占用的同时处理非常大的数据集。

比如,你的应用需要处理一个数 GB 的日志文件,同时想利用 Laravel 的集合方法来解析日志。你可以用惰性集合,在任意时刻只保留文件的一小部分在内存中,而不是把整个文件一次性读入内存:

use App\Models\LogEntry;
use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('log.txt', 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }

    fclose($handle);
})->chunk(4)->map(function (array $lines) {
    return LogEntry::fromLines($lines);
})->each(function (LogEntry $logEntry) {
    // 处理日志条目...
});

再比如,你需要遍历 10,000 个 Eloquent 模型。使用传统 Laravel 集合时,所有 10,000 个 Eloquent 模型必须同时加载到内存中:

use App\Models\User;

$users = User::all()->filter(function (User $user) {
    return $user->id > 500;
});

但是,查询构造器的 cursor() 方法返回一个 LazyCollection 实例。这让你仍然只对数据库执行一次查询,但同时在内存中只保留一个 Eloquent 模型。在这个例子中,filter 回调直到我们实际遍历每个用户时才会执行,从而大幅减少内存使用:

use App\Models\User;

$users = User::cursor()->filter(function (User $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;
    }

    fclose($handle);
});

Enumerable 契约

Collection 类上几乎所有方法在 LazyCollection 类上也可用。这两个类都实现了 Illuminate\Support\Enumerable 契约,该契约定义了以下方法:

all average avg chunk
chunkWhile collapse collect combine
concat contains containsStrict count
countBy crossJoin dd diff
diffAssoc diffKeys dump duplicates
duplicatesStrict each eachSpread every
except filter first firstOrFail
firstWhere flatMap flatten flip
forPage get groupBy has
implode intersect intersectAssoc 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 reduceInto
reject replace replaceRecursive reverse
search shuffle skip slice
sole 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 类还包含以下方法:

takeUntilTimeout()

takeUntilTimeout() 方法返回一个新的惰性集合,它会枚举值直到指定时间。过了该时间后,集合会停止枚举:

$lazyCollection = LazyCollection::times(INF)
    ->takeUntilTimeout(now()->plus(minutes: 1));

$lazyCollection->each(function (int $number) {
    dump($number);

    sleep(1);
});

// 1
// 2
// ...
// 58
// 59

为了说明此方法的用法,想象一个应用从数据库中用游标提交发票。你可以定义一个每 15 分钟运行的计划任务,最多只处理 14 分钟的发票:

use App\Models\Invoice;
use Illuminate\Support\Carbon;

Invoice::pending()->cursor()
    ->takeUntilTimeout(
        Carbon::createFromTimestamp(LARAVEL_START)->add(14, 'minutes')
    )
    ->each(fn (Invoice $invoice) => $invoice->submit());

tapEach()

each() 方法会立即对集合中的每个元素调用回调,而 tapEach() 方法只在元素被逐一拉出时才调用回调:

// 目前还没有打印任何东西...
$lazyCollection = LazyCollection::times(INF)->tapEach(function (int $value) {
    dump($value);
});

// 打印了三个元素...
$array = $lazyCollection->take(3)->all();

// 1
// 2
// 3

throttle()

throttle() 方法会对惰性集合进行限流,使每个值在指定秒数后才返回。这个方法在你与有速率限制的外部 API 交互时特别有用:

use App\Models\User;

User::where('vip', true)
    ->cursor()
    ->throttle(seconds: 1)
    ->each(function (User $user) {
        // 调用外部 API...
    });

remember()

remember() 方法返回一个新的惰性集合,它会记住已经枚举过的值,在后续枚举时不会重新获取:

// 还没有执行任何查询...
$users = User::cursor()->remember();

// 执行查询...
// 从数据库加载前 5 个用户...
$users->take(5)->all();

// 前 5 个用户从集合的缓存中获取...
// 其余的从数据库加载...
$users->take(20)->all();

withHeartbeat()

withHeartbeat() 方法允许你在惰性集合枚举期间按固定时间间隔执行回调。这对于需要定期维护的长时间运行操作特别有用,比如延长锁或发送进度更新:

use Carbon\CarbonInterval;
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('generate-reports', seconds: 60 * 5);

if ($lock->get()) {
    try {
        Report::where('status', 'pending')
            ->lazy()
            ->withHeartbeat(
                CarbonInterval::minutes(4),
                fn () => $lock->extend(CarbonInterval::minutes(5))
            )
            ->each(fn ($report) => $report->process());
    } finally {
        $lock->release();
    }
}