Collection::max
Returns the largest value in the collection, optionally read from a key or callback.
public function max(callable|string|null $callback = null): mixedParameters
| Parameter | Type | Description |
|---|---|---|
$$callbackoptional | callable|string|null= null | A key to read from each item (dot notation supported) or a callback returning the value to compare. Omit it to compare the items themselves. |
Return value
mixedThe largest value found, or null when the collection is empty.
max reduces the collection to a single largest value. With no argument it compares the items directly; with a string it reads that key from every item using data_get, so dot notation into nested data works; with a callback it compares whatever the callback returns.
When to reach for it
Summary figures over data you already have in memory — the highest invoice total in a loaded relation, the largest file in an upload batch, the newest timestamp in a parsed log. When the data lives in the database, the query builder's own max() is far cheaper, because this method requires every row to be hydrated first.
What trips people up
It returns the value, not the item that held it. To get the whole row, sort descending and take the first, or use sortByDesc() with first(). An empty collection returns null, not zero or an error, so arithmetic on the result can produce surprising output.
Comparison uses PHP's > operator, which means mixed types compare by PHP's own rules — numeric strings compare numerically, dates as strings compare lexically (fine for ISO-8601, wrong for d/m/Y). Null items are skipped before comparison, but a null extracted from a present item still takes part.
Example
When to use it
- Find the highest invoice total in an already loaded relationship.
- Get the newest ISO-8601 timestamp from a batch of parsed log lines.
- Determine the widest string in a collection to pad a console table.
Frequently asked questions
How do I get the item that holds the maximum, not just the value?
Use sortByDesc('column')->first(), because this method only ever returns the compared value. On large collections that sort is the price of knowing which row won.
What does it return for an empty collection?
null. It never returns zero and never throws, so guard the result before doing arithmetic with it.
Can I compare a nested field?
Yes — pass a dot-notation string such as 'meta.total', since values are read with data_get. A callback works too when the comparison needs computing.
More examples
Nested keys and dates
Dot notation reaches into the nested array, and ISO-8601 strings sort correctly as plain strings, so the newest deploy timestamp 2024-03-04T18:30:00Z is returned. A format like 04/03/2024 would compare lexically and give the wrong answer.
<?php
$deploys = collect([
['app' => 'api', 'meta' => ['finished_at' => '2024-03-01T09:00:00Z']],
['app' => 'web', 'meta' => ['finished_at' => '2024-03-04T18:30:00Z']],
['app' => 'jobs', 'meta' => ['finished_at' => '2024-02-27T11:15:00Z']],
]);
echo $deploys->max('meta.finished_at') . PHP_EOL;Padding a table
The callback compares string lengths rather than the strings themselves, giving 11 for 'description'. Every label is then padded to that width, so the trailing pipes line up.
<?php
$rows = collect(['id', 'description', 'qty']);
$width = $rows->max(fn (string $label) => strlen($label));
foreach ($rows as $label) {
echo str_pad($label, $width) . ' |' . PHP_EOL;
}Prefer the database when you can
Both produce the same number, but the first loads the whole table into memory. Reach for the collection method only when the rows are already loaded. This example needs a booted application and a database.
<?php
// Hydrates every row, then scans in PHP.
$slow = Order::all()->max('total');
// One aggregate query, no models built.
$fast = Order::max('total');
echo $fast;
Discussion