Collection::random
Pulls one item, or a given number of items, out of a collection at random.
Collection::random(callable|int|null $number = null, bool $preserveKeys = false): mixedParameters
| Parameter | Type | Description |
|---|---|---|
$$numberoptional | callable|int|null= null | How many items to take. Omit it to get a single item back. A closure receives the collection and must return the count. |
$$preserveKeysoptional | bool= false | Keep the original keys on the returned collection instead of reindexing from zero. Ignored when $number is null. |
Return value
mixedA single item when $number is null; a new Illuminate\Support\Collection of that many items when $number is an integer or a closure returning one.
Errors
Throws InvalidArgumentException ("You requested N items, but there are only M items available.") when the requested count exceeds the size of the collection.
Changelog
- PHP 8.x$number began accepting a closure that receives the collection and returns how many items to take, which lets you clamp the count with min().
Calling it with no arguments returns the item itself. Passing a count returns a new collection — even random(1), which gives you a one-item collection, not the item. That asymmetry is the single biggest trip-up; if you write $posts->random(1)->title you get a property error on a Collection.
Reach for it when you want a sample rather than a reordering: a featured product on a landing page, three questions out of a bank, a fixture chosen from a seeded array. If you want every item in a new order, use shuffle() instead.
Asking for more items than the collection holds is a hard error, not a silent truncation. Because collection sizes are usually dynamic, the safe pattern is the closure form, which lets you clamp the request: $c->random(fn ($items) => min(3, count($items))).
Randomness
Selection goes through PHP's Random\Randomizer, so it is cryptographically seeded and cannot be pinned with mt_srand(). Tests should assert on the shape — count, membership, no duplicates — rather than on specific values. Items are drawn without replacement, so no item can appear twice in one call.
Example
When to use it
- Show a rotating featured testimonial on a marketing page.
- Pick five questions from a large question bank for a quiz attempt.
- Choose a random fallback avatar or placeholder image for a user.
Frequently asked questions
Why does random(1) not return the item?
Because passing any count makes the method return a collection, so random(1) gives you a Collection holding one item. Call random() with no arguments for the bare item, or chain ->first() onto the one-item collection.
How do I stop random() from throwing when the collection is too small?
Pass a closure instead of an integer: random(fn ($items) => min(3, count($items))) clamps the request to the collection's actual size. Checking count() before the call works too, but the closure keeps it to one expression.
Can the same item come back twice?
No — items are drawn without replacement, so a single call can never return duplicates of the same element. If you need sampling with replacement, loop and call random() repeatedly.
More examples
Clamping the count with a closure
The closure receives the live collection, so min() caps the request at whatever is actually available and the InvalidArgumentException never fires. Because all three tags are taken, sorting the result gives a fully deterministic comparison: bool(true).
<?php
use Illuminate\Support\Collection;
$tags = collect(['php', 'laravel', 'sql']);
// random(5) would throw here — there are only three tags.
$picked = $tags->random(fn (Collection $items) => min(5, count($items)));
var_dump($picked->count()); // int(3)
var_dump($picked->sort()->values()->all() === ['laravel', 'php', 'sql']);Keeping the original keys
By default the returned collection is reindexed as a list, which destroys an id-keyed lookup. Passing true as the second argument keeps the original keys, so the sampled items can still be addressed by id.
<?php
$byId = collect([
101 => 'Desk',
102 => 'Chair',
103 => 'Lamp',
]);
$reindexed = $byId->random(2); // keys become 0, 1
$kept = $byId->random(2, true); // keys stay 101 / 102 / 103
var_dump($reindexed->keys()->all() === [0, 1]); // bool(true)
var_dump($kept->keys()->diff([101, 102, 103])->isEmpty()); // bool(true)In an application: a random featured post
Shows the realistic caveat: this loads every published post into memory before discarding all but one. For a large table prefer inRandomOrder()->first() at the database level; Collection::random is for sets you already have in memory.
<?php
namespace App\Http\Controllers;
use App\Models\Post;
class HomeController extends Controller
{
public function index()
{
$featured = Post::published()->get()->random();
return view('home', ['featured' => $featured]);
}
}
Discussion