Laravel 13.30
introduces the
chunkBy()
method, which is a convenient shorthand for the most common
chunkWhile()
use case:
$产品
->
chunkWhile
(
fn
($value, $key, $chunk) => $value
->
父母
==
$块
->
最后的
()
->
parent);
Which, you can now write as simply:
$产品
->
chunkBy
(
'parent'
(英文):
前:
chunkWhile()
and a Comparison
As shown above, before Laravel 13.30, you could use
chunkWhile()
to handle this. It takes a callback receiving the current value, its key, and the chunk being built, and it starts a new chunk whenever the callback returns false:
$lineItems
->
chunkWhile
(
fn
($value, $key, $chunk) => $value
->
订单编号
==
$块
->
最后的
()
->
订单编号(英文):
The interesting part of the line is one word (
order_id
) buried in a comparison between a value and
$chunk->last()
。
chunkBy()
takes a key or a callback and builds the comparison for you:
$lineItems
->
chunkBy
(
'order_id'
(英文):$lineItems
->
chunkBy
(
fn
($item) => $item
->
order_id);
The key resolves through
data_get()
, so dot notation reaches into nested arrays and objects:
$用户
->
chunkBy
(
'address.city'
(英文):
Adjacent, Not Grouped
The distinction from
groupBy()
is the thing to internalize, because the methods return the same shape and disagree only about your data's order:
收集
([
1
,
1
,
2
,
2
,
1
,
1
])
->
chunkBy
(
fn
($v) => $v);// [[1, 1], [2, 2], [1, 1]]收集
([
1
,
1
,
2
,
2
,
1
,
1
])
->
groupBy
(
fn
($v) => $v);// [1 => [1, 1, 1, 1], 2 => [2, 2]]
这
chunkBy()
method produces three chunks because the two runs of
1
are not next to each other. Nothing about that is a bug to work around; it is the property that makes the method cheap. If non-adjacent items with the same value need to end up together, the data is not sorted the way
chunkBy()
needs it, and either sort it first or use
groupBy()
。
Keys are preserved inside each chunk:
收集
([
'一个'
=>
1
,
‘b’
=>
1
,
‘c’
=>
2
])
->
chunkBy
(
fn
($v) => $v);// [['a' => 1, 'b' => 1], ['c' => 2]]
称呼
values()
on a chunk if you want a list.
Streaming a Sorted Query
这
chunkBy()
method is added to normal collections as well as
LazyCollection
. Since
chunkBy()
inherits
chunkWhile()
's laziness, on a lazy collection it yields each chunk as soon as the value changes and never holds more than the current chunk in memory.
Consider exporting a per-order CSV for a table with a few million line items. With
groupBy()
, every row is stored in memory before the first file is written. With a cursor and
chunkBy()
, the highest memory point will be during the largest single order:
使用
App\Models\LineItem
;使用
照明\支持\立面\存储
;LineItem
::
询问
()
->
orderBy
(
'order_id'
)
->
orderBy
(
'ID'
)
->
光标
()
->
chunkBy
(
'order_id'
)
->
每个
(
功能
($items) {$订单号
=
$items
->
第一的
()
->
order_id;
贮存
::
磁盘
(
'exports'
)
->
放
(
"orders/{
$订单号
}.csv"
,$items
->
地图
(
fn
($item) =>
内爆
(
“,”
,[$item
->
sku,$item
->
quantity,$item
->
unit_price,]))
->
内爆
(
PHP_EOL
)(英文):});
这
orderBy('order_id')
is not decoration. It is the contract
chunkBy()
runs on: the database does the sorting, in an index, and PHP does the splitting, one row at a time.
The same shape works over a log file:
使用
Illuminate\Support\LazyCollection
;惰性收集
::
制作
(
功能
(){$handle
=
打开
(
存储路径
(
'logs/laravel.log'
),
'r'
(英文):
尽管
(($line
=
fgets
($handle))
!==
错误的
){
屈服
$line;}})
->
chunkBy
(
fn
($line) =>
str_contains
($line,
'ERROR'
)
?
'错误'
:
'其他'
)
->
每个
(
功能
($block) {
// Each block is a consecutive run of error or non-error lines.});
Or over a paginated API, or a generator reading a CSV. Anywhere the source is ordered and larger than memory,
chunkBy()
turns "group by" into a streaming operation.
Two Things Worth Knowing
The comparison is loose.
The implementation compares the resolved values with
==
, 不是
===
:
收集
([
‘1’
,
1
,
1.0
])
->
chunkBy
(
fn
($v) => $v);// one chunk
For a database column that yields a consistent type this isn't an issue. For mixed input it can merge chunks you expected to be separate. Return a normalized value from the callback if needed:
$行
->
chunkBy
(
fn
($row) => (
细绳
) $row[
'代码'
]);
Two objects compare loosely as equal when they are the same class with equal properties, which is usually what you want when chunking by a value object.
The resolver runs twice per item. Each boundary check resolves the current item and re-resolves the last item of the chunk. If the callback is expensive, say a date parse or a hash, precompute the value first:
$entries
->
地图
(
fn
($entry) => [$entry,
碳
::
解析
($entry
->
logged_at)
->
日期字符串
()])
->
chunkBy
(
fn
($pair) => $pair[
1
]);
For a plain key or property lookup this is irrelevant.
边缘情况
An empty collection returns an empty collection. A single item returns one chunk containing it. Both eager and lazy collections return the same class they were called on, so
chunkBy()
在
LazyCollection
gives you a
LazyCollection
的
LazyCollection
实例。







