Collection::whereIn
Keeps only the rows whose value for a key appears in the given array.
public function whereIn(string $key, Arrayable|iterable $values, bool $strict = false): staticParameters
| Parameter | Type | Description |
|---|---|---|
$key | string | The key to read from each item, resolved with data_get so dot paths and object properties work. |
$values | \Illuminate\Contracts\Support\Arrayable|iterable | The accepted values. Arrays, collections, generators and any Arrayable are all normalised to an array first. |
$strictoptional | bool= false | Compare with === instead of ==. Passing true is equivalent to calling whereInStrict(). |
Return value
staticA new collection of the matching items, with the original keys preserved.
whereIn normalises $values to a plain array, then keeps every item whose data_get($item, $key) is found in it by in_array. It is the collection counterpart of SQL's WHERE … IN, but it runs in PHP over rows already in memory.
When to reach for it
Whitelisting against a known set: statuses you consider active, roles that grant access, IDs returned by another service. Because the second argument accepts any iterable, you can feed it another collection directly — $rows->whereIn('user_id', $admins->pluck('id')) — with no ->all() in between.
What trips people up
Comparison is loose by default, so whereIn('id', ['5']) matches the integer 5. That is usually a mercy when IDs arrive from a request as strings, but it also means whereIn('status', [0]) matches rows whose status is null or false, since PHP treats those as equal to 0. Use whereInStrict() when the types are known and should be enforced.
A row missing the key resolves to null and is dropped, unless null is itself in the list. Keys are preserved, so chain values() before JSON. And an empty $values array matches nothing.
Example
When to use it
- Keep only the rows whose status is one of several active states
- Filter a loaded collection down to a set of IDs returned by another service
- Restrict a list to roles that grant a permission
- Intersect two collections on a shared key without a database round trip
Frequently asked questions
How do I filter a collection by a list of values in Laravel?
Use whereIn with the key and the allowed values: $rows->whereIn('status', ['active', 'trial']). The values argument accepts an array, another collection, a generator or any Arrayable.
Does whereIn compare types strictly?
No, it uses loose comparison by default, so the string '5' matches the integer 5. Call whereInStrict(), or pass true as the third argument, to compare with === instead.
What happens to rows that do not have the key at all?
data_get resolves the missing key to null, so those rows are dropped unless null is in your list of values. Note that with loose comparison a list containing 0 will also match them.
More examples
Passing another collection as the value list
$privileged is a Collection, and whereIn runs it through getArrayableItems, so no ->all() or ->toArray() is needed. This is the in-memory equivalent of a join: two lists intersected on a shared key without a second database query.
<?php
$users = collect([
['id' => 1, 'name' => 'Ada', 'role_id' => 10],
['id' => 2, 'name' => 'Grace', 'role_id' => 20],
['id' => 3, 'name' => 'Alan', 'role_id' => 10],
['id' => 4, 'name' => 'Edsger', 'role_id' => 30],
]);
$privileged = collect([
['id' => 10, 'name' => 'admin', 'can_publish' => true],
['id' => 20, 'name' => 'editor', 'can_publish' => true],
['id' => 30, 'name' => 'guest', 'can_publish' => false],
])->where('can_publish', true)->pluck('id');
$publishers = $users->whereIn('role_id', $privileged);
echo $publishers->pluck('name')->implode(', '), "\n";
// Output:
// Ada, Grace, AlanMissing keys and the null trap
data_get returns null for both an explicit null and an absent key, and PHP's loose comparison makes null equal to 0 — so a filter meant to find status-zero rows quietly picks up two rows that have no status at all. whereInStrict fixes it. The last call shows there is no way to distinguish an explicit null from a missing key here.
<?php
$rows = collect([
['id' => 1, 'status' => 0],
['id' => 2, 'status' => null],
['id' => 3, 'status' => 'draft'],
['id' => 4], // key absent entirely
]);
// Loose: null == 0, so rows 2 and 4 come along for the ride.
echo $rows->whereIn('status', [0])->pluck('id')->implode(', '), "\n";
// Strict: only the real integer 0.
echo $rows->whereInStrict('status', [0])->pluck('id')->implode(', '), "\n";
// A missing key resolves to null, so it matches a null in the list.
echo $rows->whereIn('status', [null])->pluck('id')->implode(', '), "\n";
// Output:
// 1, 2, 4
// 1
// 2, 4Filtering loaded models by ID
Filtering an already-loaded collection avoids a second query when you only need to slice a result set you already have. The two calls show the trade-off: the loose form absorbs the string-versus-int mismatch that request input causes, while the strict form demands you normalise the types yourself and then rules out surprises. Needs a booted application with a database.
<?php
use App\Models\Order;
$orders = Order::with('customer')->whereMonth('created_at', 3)->get();
$flaggedIds = collect(json_decode($responseBody, true)['ids'] ?? []);
// IDs from JSON arrive as ints here, but as strings from a form —
// loose comparison covers both, which is usually what you want.
$flagged = $orders->whereIn('id', $flaggedIds)->values();
// If the IDs are trusted and typed, be explicit:
$exact = $orders->whereInStrict('id', $flaggedIds->map(fn ($id) => (int) $id))->values();
dump($flagged->count(), $exact->count());
Discussion