In Laravel, what does collect() return, and does chaining map() and filter() on it change the original collection?
answer
- a wrapper class, not an array
- Illuminate\Support\Collection
- each call hands back a new instance
- filter() with no callback drops falsy
- objects inside are shared, not copied
basics
~10 scollect() wraps an array or iterable in an Illuminate\Support\Collection. map() and filter() each return a new Collection and leave the original untouched, which is what makes long chains safe to write.
solid answer
~40 s`collect($items)` is a helper for `new Collection($items)`: it wraps an array (or any iterable, a single scalar, or `null` for an empty one) in `Illuminate\Support\Collection`. Most methods, including `map()`, `filter()` and `reject()`, build their result with `new static(...)`, so every step of a chain returns a fresh collection and the variable you started from keeps its items. That is why `$raw->filter(...)->map(...)` reads as a pipeline and why a bare `$raw->map(...)` with no assignment does nothing useful. `filter()` without a callback drops every falsy item (`0`, `''`, `null`, `false`, `[]`). The copy is shallow: if the items are objects, the old and new collections point at the same objects.
code
php · 13 lines<?php
use Illuminate\Support\Collection;
$raw = collect([4, 0, 5, null, 3]);
$kept = $raw->filter(); // [0 => 4, 2 => 5, 4 => 3]
$doubled = $kept->map(fn (int $s) => $s * 2);
$raw->map(fn ($s) => $s * 100); // result discarded: $raw is unchanged
var_dump($raw->count()); // int(5)
var_dump($doubled instanceof Collection); // bool(true)go deeper
Recall that collect() gives you an Illuminate\Support\Collection and that map(), filter() and reject() return new collections, so you must assign or chain the result.
Explain the new static() contract, what filter() drops without a callback, and why the copy is shallow when the items are models or other objects.
Point out where shared object handles let a supposedly pure chain mutate data used elsewhere, and set a team habit of returning new values from map callbacks.
Weigh collection pipelines against plain loops for readability and allocation cost in hot paths, and decide where the codebase needs explicit copies rather than trusting immutability.
## What collect() gives you `collect()` is a global helper defined in the framework's collections package. Its body is one line: it returns `new Collection($value)`, where `Collection` is **`Illuminate\Support\Collection`**. The constructor normalises whatever you pass: - an **array** is stored as-is, keys included; - another **collection**, an `Arrayable` object or any **iterable** is converted to an array first; - a **scalar** (a string, an int) is wrapped into a one-item array; - **`null`** becomes an empty collection, so `collect()` and `collect(null)` are both safe starting points. Eloquent queries hand you a subclass, `Illuminate\Database\Eloquent\Collection`, when you call `get()`, so everything below applies to query results too. ## Returning a new instance: the default contract The Laravel docs put it plainly: in general, collections are **immutable** — each method returns an entirely new `Collection` instance. Internally, `map()`, `filter()`, `reject()` and most of their siblings call a protected `newInstance()` helper that runs `new static($items)`. Two consequences follow: 1. **Chaining is safe.** Every arrow in `$answers->filter(...)->map(...)->sortBy(...)` works on the previous step's result, never on `$answers` itself. 2. **Forgetting to assign is a silent no-op.** `$answers->map(fn ($a) => $a['score'] * 10);` computes a new collection and throws it away; `$answers` is unchanged. A handful of methods break the rule on purpose — `transform()`, `push()`, `put()`, `forget()`, `pop()` and a few others change the collection in place. Knowing that list is its own interview question; for the everyday screen, the point is that `map` and `filter` are *not* on it. ## map(), filter() and reject() in detail | Method | Callback receives | Returns | Typical use | |---|---|---|---| | `map($cb)` | `($value, $key)` | new collection of the callback's return values, **same keys** | reshape each item | | `filter($cb = null)` | `($value, $key)` | new collection of items where the callback returned truthy | keep what matches | | `reject($cb)` | `($value, $key)` | new collection of items where the callback returned falsy | drop what matches | Details worth saying out loud in an interview: - **`filter()` with no callback** falls back to PHP's `array_filter`, removing every falsy value: `0`, `'0'`, `''`, `null`, `false` and empty arrays. - **`reject()`** is `filter()` with the test inverted; passing a plain value instead of a closure rejects items loosely equal to it. - The callback's second argument is the **key**, which is handy for associative data such as survey answers keyed by respondent ID. ## Shallow, not deep "Returns a new collection" describes the **container**, not the things inside it. When a collection holds objects — Eloquent models, DTOs — the new collection holds the **same object handles**. A `filter()` result and its source therefore share models: change `$model->score` through one and the other sees it. Arrays and scalars are copied by value, so for plain arrays the question never comes up. ## Getting a plain array back - `all()` returns the underlying PHP array exactly as stored. - `toArray()` also converts nested `Arrayable` items (models, other collections) to arrays. - Returning a collection from a route or controller lets Laravel serialise it to JSON for you. ## A worked example ```php <?php $answers = collect([ ['department' => 'Sales', 'score' => 4], ['department' => 'IT', 'score' => 0], ['department' => 'Sales', 'score' => 5], ]); $scored = $answers ->filter(fn (array $a) => $a['score'] > 0) ->map(fn (array $a) => $a['score'] * 20); // $answers still holds all three rows // $scored holds [0 => 80, 2 => 100] ``` The original `$answers` still has three rows, and `$scored` is a separate collection. Notice the keys `0` and `2` in the result: `filter()` keeps the original keys, which matters as soon as the result is serialised to JSON. ## Why interviewers ask this The question checks two things at once: that the candidate knows the collection is a **class with a fluent API** rather than syntax sugar over arrays, and that they understand **which calls produce new values**. Candidates who believe `map()` edits in place write code that works by accident until someone reuses the source collection further down the method.
- If map() returns a new collection, can a map callback still change the original items?Yes, when the items are objects. The new collection is a new container, but it holds the same object handles, so `$answer->score = 0` inside the callback changes the model that the original collection also holds. Return a new value or clone the object inside the callback when you need a real copy.
- What is the difference between all() and toArray() on a Laravel collection?`all()` returns the internal PHP array exactly as stored, so Eloquent models stay model objects. `toArray()` walks the items and converts anything `Arrayable` — models, nested collections — into plain arrays, which is what you want before encoding or comparing structures.
saying these in an interview costs you the question
- Says map() rewrites the collection it was called on.
- Calls $collection->map(...) without assigning the result and expects a change.
- Thinks collect() returns a plain PHP array with extra functions.
- Believes filter() with no callback returns every item unchanged.
- Assumes a new collection deep-copies the Eloquent models inside it.