定制 collectable/collection 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

collectable/collection

最新稳定版本:1.0.0

Composer 安装命令:

composer require collectable/collection

包简介

A flexible PHP collection with dot-notation and wildcard path support.

README 文档

README

A flexible PHP collection with dot-notation and wildcard path support.

Native PHP array functions are a mess — array_map, array_filter, usort, array_walk all have inconsistent argument order, most of them don't chain, and processing nested data means writing loops inside loops. Laravel's Collection solves this elegantly, but drags the entire framework with it. Collection is a standalone, zero-dependency package that works in any PHP project.

Features

  • Dot-notation & wildcards.
    • Access, write, filter, and remove deeply nested data with paths like users.*.emails.*.address — no more chaining foreach or unpacking nested arrays by hand. Wildcards work across get, set, remove, and most other methods, and you can key results by any field with *[id] syntax.
  • 100+ methods, one fluent interface.
    • Filtering (where, whereIn, whereBetween, whereContains, whereMatches), transformation (map, evolve, mapPath, flatMap, scan), aggregates (sum, avg, median, standardDeviation, percentage), multi-column sorting, pagination, set operations (diff, intersect, zip, crossJoin, symmetricDiff) — everything chains, everything returns a collection.
  • Contracts for every concern.
    • Fourteen focused interfaces — Filterable, Sortable, Aggregatable, Sliceable and more — let you type-hint against exactly what a function needs instead of depending on the concrete class. Better for testing, better for boundaries, better for static analysis.
  • Explicit about mutation.
    • Transforming methods return a new collection. Mutating methods (push, pop, splice, transform) are named and documented to make it obvious when you're changing state in place.
  • Pipelines built in.
    • pipe, pipeThrough, tap, when, unless, whenEmpty let you build readable processing chains without intermediate variables or broken-up logic.
  • Macros.
    • Add your own methods to every collection at runtime — no subclassing required.
  • ArrayAccess with dot-notation.
    • $c['user.address.city'] just works.

Installation

Install package via Composer:

composer require collectable/collection

Usage

use Collectable\Collection;

$helloWorld = new Collection(['hello', 'world'])
    ->map(fn($word) => strtoupper($word))
    ->join(' '); // "HELLO WORLD"

You also can use helper function collection():

$helloWorld = collection(...)->map(...)->join(...);

Table of Contents

Creating a Collection

make(array $items, string $wildcard = '*', string $delimiter = '.')

Create a collection from an array.

$c = Collection::make(['apple', 'banana', 'cherry']);

wrap(mixed $value)

Wrap any value into a collection. If it is already a collection it is returned as-is.

Collection::wrap('hello');       // ['hello']
Collection::wrap([1, 2, 3]);     // [1, 2, 3]
Collection::wrap($collection);   // same collection

unwrap(mixed $value)

Convert a collection (or array) back to a plain PHP array.

Collection::unwrap($collection); // plain array
Collection::unwrap([1, 2, 3]);   // [1, 2, 3]

times(int $n, callable $callback)

Generate a collection of $n items produced by a callback (index starts at 1).

Collection::times(5, fn($i) => $i * 2);
// [2, 4, 6, 8, 10]

range(int|float $from, $to, $step = 1)

Create a collection of numbers in a range.

Collection::range(1, 5);           // [1, 2, 3, 4, 5]
Collection::range(0, 1, 0.25);     // [0, 0.25, 0.5, 0.75, 1.0]

fromJson(string $json)

Create a collection from a JSON string.

$c = Collection::fromJson('{"name":"Alice","age":30}');
$c->get('name'); // 'Alice'

Reading & Writing Data

get(?string $path, mixed $default = null)

Retrieve a value by dot-notation path. Supports wildcards.

$c = Collection::make(['user' => ['name' => 'Alice', 'age' => 30]]);

$c->get('user.name');           // 'Alice'
$c->get('user.email', 'n/a');   // 'n/a'  (default)

set(string $path, mixed $value)

Set a value at a dot-notation path. Creates intermediate arrays as needed.

$c->set('user.email', 'alice@example.com');
$c->set('user.address.city', 'Moscow');

put(string $path, mixed $value)

Alias for set().

$c->put('name', 'Bob');

getOrPut(string $path, mixed $default)

Return the value if it exists; otherwise store and return $default. $default can be a callable — it is only called when the key is missing.

$token = $c->getOrPut('cache.token', fn() => bin2hex(random_bytes(16)));

pull(string $path, mixed $default = null)

Retrieve the value and then remove it from the collection.

$token = $c->pull('user.token'); // gets value and removes the key

remove(string $path) / forget(string $path)

Remove an item by dot-notation path. Both methods are identical.

$c->remove('user.password');
$c->forget('user.token');

all() / toArray()

Return all items as a plain PHP array.

$array = $c->all();

keys(?string $path = null)

Return all top-level keys, or keys of the array at a given path.

$c->keys();       // ['user', 'meta']
$c->keys('user'); // ['name', 'age']

values()

Return a new collection with consecutive integer keys (re-indexed).

Collection::make(['a' => 1, 'b' => 2])->values()->all();
// [1, 2]

collect(?string $path)

Retrieve a path and wrap the result in a new collection — useful for chaining.

$c->collect('users')->where('active', true)->pluck('name');

Checking Existence

has(string $path)

Check whether a path exists and the value is not null.

$c->has('user.name');     // true
$c->has('user.nickname'); // false

hasKey(string $path)

Check whether a path exists (even if the value is null).

$c->hasKey('user.deleted_at'); // true even when value is null

hasAny(string ...$paths)

Return true if at least one of the given paths exists.

$c->hasAny('email', 'phone'); // true if either exists

hasAll(string ...$paths)

Return true only if all of the given paths exist.

$c->hasAll('id', 'name', 'email');

isEmpty() / isNotEmpty()

Check whether the collection has no items / has at least one item.

$c->isEmpty();    // false
$c->isNotEmpty(); // true

isList()

Check whether the items form a plain indexed array (0, 1, 2, …).

Collection::make([1, 2, 3])->isList();            // true
Collection::make(['a' => 1, 'b' => 2])->isList(); // false

isAssoc()

Check whether the collection is an associative array (not a list).

Collection::make(['a' => 1])->isAssoc(); // true

count(?string $path = null)

Count top-level items, or items at a given path.

$c->count();        // number of top-level items
$c->count('users'); // count of the 'users' array

Finding Items

first(?callable $callback, mixed $default = null)

Return the first item, or the first item satisfying a callback.

$c->first();                                  // first item
$c->first(fn($u) => $u['active']);            // first active user
$c->first(fn($u) => $u['age'] > 18, 'guest'); // with default

last(?callable $callback, mixed $default = null)

Return the last item, or the last item satisfying a callback.

$c->last();
$c->last(fn($u) => $u['active']);

firstOrFail(?callable $callback)

Return the first matching item or throw \RuntimeException if none found.

$user = $users->firstOrFail(fn($u) => $u['id'] === 42);

firstWhere(string $path, $operatorOrValue, $value = null)

Return the first item where the value at $path matches.

$users->firstWhere('role', 'admin');
$users->firstWhere('age', '>=', 18);

firstKey(?callable $callback) / lastKey(?callable $callback)

Return the key of the first (or last) matching item.

$c->firstKey();                       // key of first item
$c->firstKey(fn($v) => $v['active']); // key of first active item

value(string $path, mixed $default = null)

Get the value at $path from the first item in the collection.

$users->value('email'); // email of the first user

find(string $path, mixed $value, mixed $default = null)

Return the first item where $path === $value.

$users->find('id', 42);
$users->find('email', 'alice@example.com');

search(mixed $valueOrCallback)

Return the key of the first matching item, or false.

$c->search('Alice');
$c->search(fn($v) => $v['active']);

sole(string|callable|null $pathOrCallback = null, mixed $value = null)

Return the single matching item or throw if there are zero or multiple matches.

$user = $users->sole('id', 42);

hasSole(...)

Return true if exactly one item matches (boolean variant of sole()).

$users->hasSole('role', 'superadmin');

after(mixed $valueOrCallback, mixed $default = null)

Return the item immediately after the given value.

Collection::make([1, 2, 3, 4])->after(2); // 3

before(mixed $valueOrCallback, mixed $default = null)

Return the item immediately before the given value.

Collection::make([1, 2, 3, 4])->before(3); // 2

Filtering

filter(string|callable $pathOrCallback, mixed $value = null)

Keep only items that satisfy a callback or a path condition.

$c->filter(fn($u) => $u['active']);
$c->filter('active');        // items where active is truthy
$c->filter('role', 'admin'); // items where role === 'admin'

reject(string|callable $pathOrCallback, mixed $value = null)

Keep items that do not satisfy the condition (inverse of filter()).

$c->reject('active');
$c->reject('role', 'guest');

where(string $path, $operatorOrValue, $value = null)

Filter by a dot-notation path with an optional comparison operator.

Supported operators: =, ==, ===, !=, !==, <>, >, >=, <, <=

$users->where('active');
$users->where('role', 'admin');    // loose ==
$users->where('age', '>=', 18);
$users->where('status', '===', 1); // strict

whereIn(string $path, array $values) / whereNotIn(string $path, array $values)

Filter by whether the path value is (or is not) in a list.

$users->whereIn('role', ['admin', 'editor']);
$users->whereNotIn('status', ['banned', 'inactive']);

whereNull(?string $path) / whereNotNull(?string $path)

Filter items where the path value is (or is not) null.

$users->whereNull('deleted_at');    // soft-deleted
$users->whereNotNull('deleted_at'); // active

whereBetween(string $path, array $values) / whereNotBetween(...)

Filter by an inclusive range [$min, $max].

$products->whereBetween('price', [10, 100]);
$users->whereNotBetween('age', [18, 65]);

whereContains(string $path, string $needle, bool $ignoreCase = false)

Filter items where the string value at $path contains $needle.

$users->whereContains('name', 'ali');
$users->whereContains('email', '@gmail', true); // case-insensitive

whereStartsWith(string $path, string $prefix, bool $ignoreCase = false)

Filter items where the string value starts with a prefix.

$users->whereStartsWith('name', 'Al');

whereEndsWith(string $path, string $suffix, bool $ignoreCase = false)

Filter items where the string value ends with a suffix.

$users->whereEndsWith('email', '@gmail.com');

whereMatches(string $path, string $regex)

Filter items where the string value matches a regular expression.

$users->whereMatches('code', '/^[A-Z]{2}\d{3}$/');

whereInstanceOf(string|array $class, ?string $path = null)

Filter items by class or interface.

$items->whereInstanceOf(User::class);
$items->whereInstanceOf([Admin::class, Moderator::class]);

whereStrict(string $path, mixed $value)

Strict === version of where().

$users->whereStrict('status', 1); // int 1 only, not '1'

unique(string|callable|null $by = null) / uniqueStrict(...)

Remove duplicate items. Optionally group by a path or callback.

$c->unique();
$users->unique('email');
$users->unique(fn($u) => $u['domain']);

duplicates(string|callable|null $by = null) / duplicatesStrict(...)

Return only the duplicate items.

$users->duplicates('email'); // users with a repeated email

Mapping & Transforming

map(callable $callback)

Apply a callback to every item. Returns a new collection.

$prices = $c->map(fn($item) => $item['price'] * 1.2);

filterMap(callable $callback)

Map and discard null/false results in a single pass.

$emails = $users->filterMap(fn($u) => $u['active'] ? $u['email'] : null);

mapWithKeys(callable $callback)

Rebuild the collection as key -> value pairs.

$lookup = $users->mapWithKeys(fn($u) => [$u['id'] => $u['name']]);
// [1 => 'Alice', 2 => 'Bob']

mapKeys(callable $callback)

Transform only the keys, keeping values unchanged.

$c->mapKeys(fn($k) => strtoupper($k));

mapToGroups(callable $callback)

Map items to [$key => $value] pairs, then group by key.

$c->mapToGroups(fn($u) => [$u['department'] => $u['name']]);

mapSpread(callable $callback) / eachSpread(callable $callback)

Spread each sub-array item as separate arguments.

Collection::make([[1, 2], [3, 4]])->mapSpread(fn($a, $b) => $a + $b);
// [3, 7]

mapInto(string $class)

Map items into instances of $class (passes each item to the constructor).

$objects = $items->mapInto(ProductDTO::class);

mapBy(string|callable $key) / keyBy(...)

Re-key the collection by a path or callback (duplicate keys overwrite).

$byId = $users->mapBy('id');
// [1 => [...user1...], 2 => [...user2...]]

mapPath(string $path, callable $callback)

Transform only the value at a specific path inside each item.

$c->mapPath('price', fn($p) => round($p * 1.2, 2));
$c->mapPath('meta.slug', 'strtolower');

evolve(array $transformers)

Apply multiple path transformations in a single call.

$c->evolve([
    'price'     => fn($p) => round($p * 1.2, 2),
    'meta.slug' => 'strtolower',
    'name'      => 'trim',
]);

transform(callable $callback)

Like map() but mutates the collection in-place.

$c->transform(fn($v) => $v * 2);

flatMap(callable $callback)

Map each item and collapse the results one level.

$emails = $users->flatMap(fn($u) => $u['emails']);

pluck(string $path, ?string $key = null)

Extract a single field from every item.

$names = $users->pluck('name');
$byId  = $users->pluck('name', 'id'); // [1 => 'Alice', 2 => 'Bob']

select(array $fields)

Pick multiple fields from each item, supporting dot-notation paths.

$users->select(['id', 'name', 'user.email']);

only(array $keys)

Return a new collection with only the specified top-level keys.

$c->only(['id', 'name']);

except(array $keys)

Return a new collection with the specified top-level keys removed.

$c->except(['password', 'token']);

flatten(int|float $depth = INF)

Flatten nested arrays into a single indexed array.

Collection::make([[1, [2, 3]], [4]])->flatten();
// [1, 2, 3, 4]

Collection::make([[1, [2, 3]], [4]])->flatten(1);
// [1, [2, 3], 4]

collapse()

Collapse one level of nested arrays into a single flat array.

Collection::make([[1, 2], [3, 4]])->collapse()->all();
// [1, 2, 3, 4]

dot(string $prefix = '')

Flatten a nested array to dot-notation key -> value pairs.

Collection::make(['user' => ['name' => 'Alice', 'age' => 30]])->dot()->all();
// ['user.name' => 'Alice', 'user.age' => 30]

undot()

Convert dot-notation keys back to a nested array.

Collection::make(['user.name' => 'Alice', 'user.age' => 30])->undot()->all();
// ['user' => ['name' => 'Alice', 'age' => 30]]

flip()

Swap keys and values.

Collection::make(['a' => 1, 'b' => 2])->flip()->all();
// [1 => 'a', 2 => 'b']

reverse(bool $preserveKeys = false)

Return items in reverse order.

Collection::make([1, 2, 3])->reverse()->all(); // [3, 2, 1]

transpose()

Transpose a matrix — rows become columns.

Collection::make([[1, 2, 3], [4, 5, 6]])->transpose()->all();
// [[1, 4], [2, 5], [3, 6]]

scan(callable $callback, mixed $initial = null)

Running fold — like reduce() but returns every intermediate value.

Collection::make([1, 2, 3, 4])->scan(fn($carry, $v) => $carry + $v, 0)->all();
// [1, 3, 6, 10]

reduce(callable $callback, mixed $initial = null)

Reduce the collection to a single value.

$total = $c->reduce(fn($carry, $item) => $carry + $item, 0);

toTree(string $idKey, string $parentKey, string $childrenKey = 'children', $rootId = null)

Build a nested tree structure from a flat list.

$tree = $categories->toTree('id', 'parent_id');
// Each item gets a 'children' key containing its sub-items

Sorting

sort(?callable $callback = null)

Sort items. With no callback uses natural ordering.

$c->sort();
$c->sort(fn($a, $b) => $a <=> $b);

sortDesc(?callable $callback = null)

Sort in descending order.

$c->sortDesc();

sortBy(string|callable|array $by, bool $desc = false)

Sort items by a dot-notation path, callback, or multiple columns.

$users->sortBy('name');
$users->sortBy('age', desc: true);
$products->sortBy(['category', 'price' => 'desc']);

sortByDesc(string|callable $by)

Shorthand for sortBy($by, desc: true).

$products->sortByDesc('price');

sortKeys(?callable $callback = null) / ksort(?callable $callback = null)

Sort by key.

$c->sortKeys();

sortKeysDesc()

Sort by key in descending order.

$c->sortKeysDesc();

sortKeysUsing(callable $comparator)

Sort by key using a custom comparator.

$c->sortKeysUsing('strnatcasecmp');

Grouping & Keying

groupBy(string|callable $by)

Group items into sub-collections by a path or callback.

$byRole = $users->groupBy('role');
// ['admin' => Collection, 'editor' => Collection]

$users->groupBy(fn($u) => $u['active'] ? 'active' : 'inactive');

countBy(string|callable|null $by = null)

Count occurrences per group.

$c->countBy();             // count each unique value
$users->countBy('role');   // ['admin' => 2, 'editor' => 5]

partition(callable $callback)

Split the collection into two: passing and failing items.

[$admins, $others] = $users->partition(fn($u) => $u['role'] === 'admin');

Aggregates

sum(string|callable|null $pathOrCallback = null)

Sum all items, a plucked path, or a computed value.

$c->sum();
$orders->sum('total');
$orders->sum(fn($o) => $o['price'] * $o['qty']);

product(string|callable|null $pathOrCallback = null)

Multiply all items together.

$c->product();
$items->product('qty');

avg(string|callable|null $pathOrCallback = null) / average(...)

Return the average value.

$scores->avg('score');

min(string|callable|null $pathOrCallback = null) / max(...)

Return the minimum or maximum value.

$products->min('price');
$products->max('price');

minBy(string|callable $by) / maxBy(string|callable $by)

Return the item with the minimum or maximum value at a path.

$cheapest = $products->minBy('price'); // returns the whole item
$priciest = $products->maxBy('price');

median(?string $path = null)

Return the median value.

$scores->median('score');

mode(?string $path = null)

Return the most frequently occurring value(s).

$c->mode('score'); // Collection of the most common scores

standardDeviation(?string $path = null, bool $sample = false)

Return the population (or sample) standard deviation.

$scores->standardDeviation('score');
$scores->standardDeviation('score', sample: true);

percentage(callable $callback, int $precision = 2)

Return the percentage of items passing a test (0–100.0).

$users->percentage(fn($u) => $u['active']); // e.g. 75.50

contains(mixed $pathOrValueOrCallback, mixed $value = null)

Check if the collection contains a value, or a path matches a value.

$c->contains('Alice');
$users->contains('role', 'admin');
$c->contains(fn($v) => $v > 10);

doesntContain(...) / some(...) / containsStrict(...)

Variants of contains() — inverse, alias, and strict comparison.

$c->doesntContain('admin');
$users->some('role', 'admin');
$users->containsStrict('status', 1);

every(string|callable $pathOrCallback, mixed $value = null)

Check if all items satisfy a condition.

$users->every(fn($u) => $u['active']);
$users->every('role', 'admin');

Slicing & Pagination

slice(int $offset, ?int $length = null)

Return a sub-range of items.

$c->slice(0, 10); // first 10 items
$c->slice(-3);    // last 3 items

take(int $n)

Return the first $n (or last, if negative) items.

$c->take(3);  // first 3
$c->take(-2); // last 2

skip(int $n)

Skip the first $n items.

$c->skip(2); // [3, 4, 5] from [1, 2, 3, 4, 5]

forPage(int $page, int $perPage)

Return items for a specific page (1-based).

$c->forPage(2, 15); // items 16–30

paginate(int $perPage, int $page = 1)

Return a pagination envelope with metadata.

$result = $users->paginate(15, 2);
// ['data' => Collection, 'total' => 100, 'per_page' => 15,
//  'current_page' => 2, 'last_page' => 7, 'from' => 16, 'to' => 30]

chunk(int $size)

Split the collection into chunks of $size. Returns a collection of collections.

$c->chunk(3); // [[1,2,3], [4,5,6], [7]]

chunkWhile(callable $callback)

Chunk items into consecutive groups while the condition holds.

Collection::make([1, 2, 3, 7, 8, 11])
    ->chunkWhile(fn($v, $prev) => $v === $prev + 1);
// [[1,2,3], [7,8], [11]]

sliding(int $size, int $step = 1)

Create a sliding window over the collection.

Collection::make([1, 2, 3, 4, 5])->sliding(3)->map->all();
// [[1,2,3], [2,3,4], [3,4,5]]

split(int $n) / splitIn(int $n)

Split into $n roughly equal groups.

Collection::make([1, 2, 3, 4, 5])->split(3);
// [[1, 2], [3, 4], [5]]

nth(int $step, int $offset = 0)

Return every $step-th item.

$c->nth(2);    // 1st, 3rd, 5th …
$c->nth(3, 1); // 2nd, 5th, 8th …

pad(int $size, mixed $value = null)

Pad the collection to $size with a value. Negative pads at the start.

Collection::make([1, 2])->pad(5, 0)->all(); // [1, 2, 0, 0, 0]
Collection::make([1, 2])->pad(-5, 0)->all(); // [0, 0, 0, 1, 2]

Set Operations

merge(array|Collection $items)

Merge another array or collection (top-level).

$c->merge(['d', 'e']);

mergeRecursive(array|Collection $items)

Deep-merge arrays — repeated string keys produce nested arrays.

$c->mergeRecursive(['meta' => ['extra' => true]]);

replace(array|Collection $items) / replaceRecursive(...)

Replace values using PHP's array_replace semantics.

$c->replace([0 => 'x']);
$c->replaceRecursive(['user' => ['name' => 'Bob']]);

concat(array|Collection $items)

Append all values, always re-indexing (numeric keys never overwrite).

$c->concat(['d', 'e']);

union(array|Collection $items)

Fill in missing keys from $items without overwriting existing ones.

$c->union(['a' => 1, 'b' => 99]); // 'b' added only if not present

diff(array|Collection $items)

Return items not present in $items.

Collection::make([1, 2, 3, 4])->diff([2, 4])->all(); // [1, 3]

diffAssoc(...) / diffKeys(...)

Diff comparing both key+value or keys only.

$c->diffAssoc(['a' => 1]);
$c->diffKeys(['a' => 'x', 'c' => 'y']);

diffUsing(array|Collection $items, callable $comparator)

Diff using a custom comparator function.

$objects->diffUsing($other, fn($a, $b) => $a->id <=> $b->id);

intersect(array|Collection $items)

Return items present in both collections.

Collection::make([1, 2, 3])->intersect([2, 3, 4])->all(); // [2, 3]

intersectByKeys(...) / intersectAssoc(...) / intersectUsing(...)

Intersection variants by keys, key+value, or custom comparator.

symmetricDiff(array|Collection $items)

Return items present in one collection but not both (A ∖ B) ∪ (B ∖ A).

$old->symmetricDiff($new); // items that were added or removed

zip(array ...$arrays)

Pair items from multiple arrays together.

Collection::make([1, 2, 3])->zip(['a', 'b', 'c'])->all();
// [[1,'a'], [2,'b'], [3,'c']]

unzip()

Unzip a collection of tuples into separate collections (inverse of zip()).

[$numbers, $letters] = Collection::make([[1,'a'],[2,'b']])->unzip();

crossJoin(array ...$arrays)

Return the cartesian product.

Collection::make([1, 2])->crossJoin(['a', 'b'])->all();
// [[1,'a'], [1,'b'], [2,'a'], [2,'b']]

combine(array|Collection $values)

Use this collection as keys and $values as values.

Collection::make(['a', 'b'])->combine([1, 2])->all();
// ['a' => 1, 'b' => 2]

multiply(int $times)

Repeat every item $times times.

Collection::make([1, 2])->multiply(3)->all(); // [1, 2, 1, 2, 1, 2]

shuffle()

Return a new collection with items in random order.

$c->shuffle();

random(int $n = 1)

Return $n random items. Returns the item directly when $n === 1.

$c->random();  // one random item
$c->random(3); // Collection of 3 random items

Iteration & Pipelines

each(callable $callback)

Iterate over every item. Return false to stop early.

$c->each(function ($value, $key) {
    echo "$key: $value\n";
});

eachSpread(callable $callback)

Iterate by spreading each sub-array as arguments.

Collection::make([['Alice', 30], ['Bob', 25]])
    ->eachSpread(fn($name, $age) => print "$name is $age\n");

takeUntil(mixed $valueOrCallback)

Take items until the condition is met.

Collection::make([1, 2, 3, 4, 5])->takeUntil(fn($v) => $v > 3)->all();
// [1, 2, 3]

takeWhile(mixed $valueOrCallback)

Take items while the condition holds.

Collection::make([1, 2, 3, 4, 5])->takeWhile(fn($v) => $v < 4)->all();
// [1, 2, 3]

skipUntil(mixed $valueOrCallback)

Skip items until the condition is met, then return the rest.

Collection::make([1, 2, 3, 4, 5])->skipUntil(fn($v) => $v >= 3)->all();
// [3, 4, 5]

skipWhile(mixed $valueOrCallback)

Skip items while the condition holds, then return the rest.

Collection::make([1, 2, 3, 4, 5])->skipWhile(fn($v) => $v < 3)->all();
// [3, 4, 5]

pipe(callable $callback)

Pass the collection to a callback and return whatever the callback returns.

$total = $c->pipe(fn($c) => $c->sum('price'));

pipeInto(string $class)

Pass the collection as the first argument to a class constructor.

$presenter = $c->pipeInto(UserPresenter::class);

pipeThrough(array $pipes)

Pass the collection through an ordered array of callables.

$result = $c->pipeThrough([
    fn($c) => $c->filter('active'),
    fn($c) => $c->sortBy('name'),
    fn($c) => $c->values(),
]);

tap(callable $callback)

Call a callback for side-effects, then return $this (stays in chain).

$c->filter('active')
  ->tap(fn($c) => logger()->info('Active users: ' . $c->count()))
  ->sortBy('name');

when(bool|callable $condition, callable $then, ?callable $else = null)

Execute $then when the condition is truthy.

$c->when($isAdmin, fn($c) => $c->merge($adminItems));
$c->when(fn($c) => $c->isNotEmpty(), fn($c) => $c->sortBy('name'));

unless(bool|callable $condition, callable $then, ?callable $else = null)

Execute $then when the condition is falsy (inverse of when()).

$c->unless($c->isEmpty(), fn($c) => $c->sortBy('name'));

whenEmpty(callable $then) / whenNotEmpty(callable $then)

Conditional execution based on whether the collection is empty.

$c->whenEmpty(fn($c) => $c->push('default'));
$c->whenNotEmpty(fn($c) => $c->sortBy('name'));

ensure(string|array $types)

Assert that every item is of the expected type; throw otherwise.

$c->ensure('int');
$c->ensure(['int', 'float']);
$c->ensure(User::class);

Mutation

push(mixed $value)

Append a value to the end.

$c->push('new item');

prepend(mixed $value, mixed $key = null)

Add a value to the beginning.

$c->prepend(0);
$c->prepend('home', 'first');

insert(int $index, mixed $value)

Insert a value at a specific index (non-mutating, returns new collection).

$c->insert(2, 'x');  // inject at position 2
$c->insert(-1, 'x'); // before the last item

pop()

Remove and return the last item.

$last = $c->pop();

shift()

Remove and return the first item.

$first = $c->shift();

splice(int $offset, ?int $length = null, array $replacement = [])

Remove items starting at $offset, optionally replacing them. Mutates the collection and returns the removed items as a new collection.

$removed = $c->splice(2);            // removes from index 2 to end
$removed = $c->splice(1, 2);         // removes 2 items at index 1
$removed = $c->splice(1, 2, ['x']);  // removes 2 and inserts 'x'

clear()

Remove all items from the collection.

$c->clear();

copy()

Return a deep clone of the collection.

$clone = $c->copy();

join(string $glue, ?string $finalGlue = null) / implode(string $value, ?string $glue = null)

Join all items into a string.

$c->join(', '); // 'Alice, Bob, Carol'
$c->join(', ', ' and '); // 'Alice, Bob and Carol'

$users->implode('name', ', '); // pluck 'name' then join
$c->implode(', '); // join scalar items

Serialization

toJson(int $flags = 0) / toPrettyJson()

Convert the collection to a JSON string.

$c->toJson();
$c->toPrettyJson();

fromJson(string $json) (static)

Create a collection from a JSON string.

$c = Collection::fromJson('{"a":1,"b":2}');

__toString()

Automatically called when the collection is cast to a string (returns JSON).

echo $c; // prints JSON

Debugging

dump()

Dump the collection contents and return $this (stays in chain).

$c->filter('active')->dump()->sortBy('name');

dd()

Dump the collection and terminate execution.

$c->dd();

Dot-Notation & Wildcards

The collection understands dot-notation paths for deeply nested data and supports * wildcards to operate on multiple items at once.

$c = Collection::make([
    'users' => [
        ['id' => 1, 'name' => 'Alice', 'role' => 'admin',  'emails' => [['address' => 'alice@a.com']]],
        ['id' => 2, 'name' => 'Bob',   'role' => 'editor', 'emails' => [['address' => 'bob@b.com'], ['address' => 'bob2@b.com']]],
    ],
]);

// Read a single path
$c->get('users.0.name'); // 'Alice'

// Wildcard: get all names (keyed by array index)
$c->get('users.*.name'); // [0 => 'Alice', 1 => 'Bob']

// Nested wildcards: all email addresses
$c->get('users.*.emails.*.address');
// [0 => [0 => 'alice@a.com'], 1 => [0 => 'bob@b.com', 1 => 'bob2@b.com']]

// Set a value on every item
$c->set('users.*.active', true);

// Remove a field from every item
$c->remove('users.*.role');

Wildcard with a custom key: *[field]

By default * uses the numeric array index as the result key. You can instruct it to use the value of a specific field from each item instead by writing *[field] in the path segment.

// Get names keyed by user id instead of 0, 1, 2 …
$c->get('users.*[id].name');
// [1 => 'Alice', 2 => 'Bob']

// Nested wildcards — outer key comes from the user id
$c->get('users.*[id].emails.*.address');
// [1 => [0 => 'alice@a.com'], 2 => [0 => 'bob@b.com', 1 => 'bob2@b.com']]

If the specified key field is missing from an item, that item falls back to its numeric index in the result.

Note: the key field name in *[field] must be a simple top-level field (not a dot-notation path), because the dot delimiter is also used inside the brackets.

ArrayAccess Support

The collection implements ArrayAccess, so you can use bracket syntax with dot-notation paths.

$c['user.name'];           // same as get('user.name')
$c['user.name'] = 'Alice'; // same as set('user.name', 'Alice')
isset($c['user.name']);    // same as has('user.name')
unset($c['user.name']);    // same as remove('user.name')

Contracts

Every aspect of Collection is described by a focused PHP interface in Collectable\Contracts. You can type-hint against any single interface instead of the concrete class — useful for testing, custom implementations, and keeping dependencies minimal.

Collectable\Contracts
├── Collectable          <- full contract (extends all below)
├── Accessable           <- get / set / has / remove / pull
├── Arrayable            <- all / values / keys / flatten / groupBy …
├── Queryable            <- first / last / find / search / sole …
├── Filterable           <- filter / reject / where* / unique …
├── Mappable             <- map / pluck / reduce / evolve / scan …
├── Aggregatable         <- sum / avg / min / max / every / countBy …
├── Sliceable            <- slice / take / chunk / paginate / sliding …
├── Combinable           <- merge / diff / intersect / zip / crossJoin …
├── Sortable             <- sort / sortBy / sortKeys …
├── Mutable              <- push / pop / shift / splice / insert …
├── Pipeable             <- pipe / tap / when / unless / ensure …
├── Jsonable             <- toJson / fromJson
└── Debuggable           <- dump / dd

Collection implements Collectable, which extends all of the above plus the standard PHP interfaces (ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Stringable).

Type-hinting against a specific interface

Instead of depending on the concrete Collection class you can type-hint against the narrowest interface that covers what you actually need.

use Collectable\Contracts\Aggregatable;
use Collectable\Contracts\Filterable;
use Collectable\Contracts\Sortable;

// This function only needs to filter and sort — no concrete class required.
function getTopUsers(Filterable&Sortable $users, int $limit): array
{
    return $users
        ->where('active', true)
        ->sortByDesc('score')
        ->take($limit)
        ->all();
}

// Works with Collection out of the box:
getTopUsers(Collection::make($data), 10);

Full contract reference

Interface Key methods
Accessable get, set, put, has, hasKey, hasAny, hasAll, remove, forget, pull, getOrPut
Arrayable all, toArray, values, keys, count, isEmpty, isList, isAssoc, flatten, collapse, dot, undot, only, except, flip, reverse, groupBy, partition, join, implode, shuffle, random, multiply, copy, transpose, toTree
Queryable first, last, firstOrFail, firstWhere, firstKey, lastKey, value, after, before, search, find, sole, hasSole
Filterable filter, reject, where, whereIn, whereNotIn, whereNull, whereNotNull, whereBetween, whereNotBetween, whereStrict, whereInstanceOf, whereContains, whereStartsWith, whereEndsWith, whereMatches, unique, uniqueStrict, duplicates, duplicatesStrict
Mappable map, filterMap, mapWithKeys, mapKeys, mapToGroups, mapSpread, mapInto, mapBy, keyBy, mapPath, evolve, transform, flatMap, pluck, select, scan, reduce
Aggregatable sum, product, avg, average, min, max, minBy, maxBy, median, mode, standardDeviation, percentage, countBy, contains, doesntContain, some, containsStrict, every
Sliceable slice, take, skip, forPage, paginate, chunk, chunkWhile, sliding, split, splitIn, nth, pad, takeUntil, takeWhile, skipUntil, skipWhile
Combinable merge, mergeRecursive, replace, replaceRecursive, concat, union, diff, diffAssoc, diffKeys, diffUsing, intersect, intersectByKeys, intersectAssoc, intersectUsing, symmetricDiff, zip, unzip, crossJoin, combine
Sortable sort, sortDesc, sortBy, sortByDesc, sortKeys, sortKeysDesc
Mutable push, prepend, insert, pop, shift, splice, clear
Pipeable each, eachSpread, pipe, pipeInto, pipeThrough, tap, when, unless, whenEmpty, whenNotEmpty, unlessEmpty, unlessNotEmpty, ensure
Jsonable toJson, toPrettyJson, fromJson
Debuggable dump, dd

Macros

You can add your own methods to every collection at runtime via macro().

Collection::macro('toUpperCase', function () {
    return $this->map(fn($v) => strtoupper($v));
});

Collection::make(['hello', 'world'])->toUpperCase()->all();
// ['HELLO', 'WORLD']

See Macroable the source code for the full list of methods.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-09

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固