This is a documentation for Board Game Arena: play board games online !
Collection
"Collection" is a PHP helper for working with keyed groups of values. It extends PHP's ArrayObject, so it can be iterated, counted, and accessed with array syntax, while also providing convenient methods for filtering, transforming, grouping, and querying its contents.
Collections are used throughout the BGA framework. In particular, ItemManager methods that return several game objects return a typed Collection keyed by item ID.
With Collection, you can:
- Keep the keys of a set of items while filtering, sorting, or slicing it.
- Find, count, and test values with callbacks.
- Transform or reduce values without writing explicit loops.
- Query arrays and objects by field, array key, or getter.
- Group items into nested collections.
- Convert a collection to either a keyed PHP array or a zero-based list.
Collection overview
Creating a collection
Import the class and pass an array to its constructor:
use Bga\GameFramework\Helpers\Collection;
$cards = new Collection([
12 => ['type' => 'heart', 'value' => 7],
18 => ['type' => 'spade', 'value' => 10],
25 => ['type' => 'heart', 'value' => 3],
]);
The keys can be integers or strings, and the values can be of any type. In PHPDoc, use the generic notation Collection<Card> or Collection<int> to help static analysis and IDE completion:
/** @var Collection<Card> $hand */ $hand = $this->cards->getItemsInLocation(['hand', $playerId]);
Using a collection as an ArrayObject
Because Collection extends ArrayObject, ordinary PHP operations work as expected:
foreach ($cards as $id => $card) {
// ...
}
$card = $cards[18];
$numberOfCards = count($cards);
$cards[31] = $anotherCard;
unset($cards[12]);
Array access changes that Collection instance. By contrast, Collection methods such as filter(), map(), sort(), and update() return a new Collection and leave the original Collection unchanged.
Keys and native arrays
Collection distinguishes between a keyed array and a list:
$cards->keys(); // [12, 18, 25] $cards->all(); // [12 => $card1, 18 => $card2, 25 => $card3] $cards->values(); // [$card1, $card2, $card3]
Most methods preserve existing keys. Use values() when a zero-based PHP array is needed, especially when sending a list to the client. Otherwise, non-consecutive integer keys are encoded by JSON as an object rather than an array.
Callbacks
Predicates and mapping callbacks can receive the value and, optionally, its key:
$highCards = $cards->filter(
fn(Card $card, int|string $id): bool => $card->value >= 10
);
$labels = $cards->map(
fn(Card $card): string => $card->type . '-' . $card->value
);
Callbacks may declare only the value when the key is not needed. reduce() is different: its callback receives the accumulator and then the value, without the key.
Reading fields from arrays and objects
The methods pluck(), where(), countWhere(), whereNot(), whereNull(), sortBy(), and groupBy() can read a named value from either an array or an object.
For 'score', Collection looks for:
- The
'score'key when the item is an array. - A
getScore()method when the item is an object. - A
$scoreproperty when there is no getter.
Getters therefore take precedence over properties. Object properties used this way should be public.
class PlayerResult
{
public function __construct(
public int $playerId,
private int $points,
) {
}
public function getScore(): int
{
return $this->points;
}
}
$winners = $results
->where('score', 20)
->pluck('playerId');
Common examples
Filtering and transforming
// Keep the original item IDs as keys. $playable = $hand->filter(fn(Card $card): bool => $card->isPlayable()); // Transform each card, still using the original item IDs as keys. $values = $hand->map(fn(Card $card): int => $card->value); // Produce a zero-based list for game data or a notification. $args['cards'] = $playable->values();
Finding and testing
$ace = $hand->find(fn(Card $card): bool => $card->value === 14); $aceId = $hand->findKey(fn(Card $card): bool => $card->value === 14); $hasHeart = $hand->some(fn(Card $card): bool => $card->suit === 'heart'); $allVisible = $hand->every(fn(Card $card): bool => $card->visible); $visibleCount = $hand->count(fn(Card $card): bool => $card->visible);
find() and findKey() return null when no value matches. some() returns false on an empty Collection, while every() returns true on an empty Collection.
Querying fields
$hearts = $cards->where('suit', 'heart');
$redCards = $cards->where('suit', ['heart', 'diamond']);
$notDiscarded = $cards->whereNot('location', 'discard');
$withoutOwner = $cards->whereNull('ownerId');
$numberOfAces = $cards->countWhere('value', 14);
// '%' is a case-insensitive wildcard.
$playerLocations = $cards->where('location', 'hand%');
Field comparisons are strict by default. Pass false as the third argument of where(), whereNot(), or countWhere() to use loose comparison:
$cards->where('value', '10', strict: false);
When the searched value is an array, it is treated as a set of allowed values. When it is a string containing %, the percent signs match any sequence of characters and the match is case-insensitive.
Sorting and grouping
$byValue = $cards->sortBy('value');
$highestFirst = $cards->sortBy('value', 'DESC');
$bySuit = $cards->groupBy('suit');
$hearts = $bySuit['heart']; // A Collection of hearts with their original keys.
$byParity = $cards->groupBy(
fn(Card $card): string => $card->value % 2 === 0 ? 'even' : 'odd'
);
groupBy() returns a Collection whose values are themselves Collections. A group key must be an integer or string. Both the order of the groups and the original keys inside each group are retained.
Updating object values
update() returns a Collection in which the named field has been changed on every item. Objects are cloned first, so the objects in the original Collection are not modified. Arrays are copied.
$hiddenCards = $cards->update('visible', false);
For an object field named 'visible', Collection calls setVisible(false) when that setter exists. Otherwise it writes to the public $visible property. It throws an exception if the item is scalar or the field cannot be updated.
update() only changes the returned PHP objects; it does not automatically persist ItemManager objects. Use ItemManager's update methods when changes must be stored in the database.
Collection reference
Construction and conversion
new Collection( array|object $array = [], int $flags = 0, string $iteratorClass = ArrayIterator::class )
Creates a Collection from an array or object. The optional flags and iterator class are passed to ArrayObject.
keys(): array
Returns the keys in their current order as a zero-based array of integers and strings.
values(): array
Returns the values as a zero-based native PHP array. Original keys are discarded.
all(): array
Returns a native PHP array containing all values with their keys preserved.
Inspecting values
isEmpty(): bool
Returns true when the Collection contains no items.
first(): mixed
Returns the first value in insertion order, or null for an empty Collection.
last(): mixed
Returns the last value in insertion order, or null for an empty Collection.
has( int|string $key ): bool
Returns whether the supplied key exists. This checks keys, including keys whose value is null.
contains( mixed $value ): bool
Returns whether the supplied value occurs in the Collection. This uses PHP's non-strict in_array() comparison.
random(): mixed
Returns a random value, or null for an empty Collection.
find( callable $fn ): mixed
Returns the first value for which $fn($value, $key) is true, or null if there is no match.
findKey( callable $fn ): int|string|null
Returns the key of the first value for which $fn($value, $key) is true, or null if there is no match.
count( ?callable $fn = null ): int
Without a callback, returns the total number of values. With a callback, returns the number for which $fn($value, $key) is true. The usual count($collection) syntax also returns the total.
some( callable $fn ): bool
Returns whether at least one value satisfies $fn($value, $key).
every( callable $fn ): bool
Returns whether every value satisfies $fn($value, $key).
Creating and combining collections
add( mixed $object, ?int $id = null ): Collection
Returns a new Collection with the object stored under $id. If no ID is supplied, add() reads the object's id array key, getId() method, or $id property. An existing value under the same key is replaced.
diff( Collection $remove, ?callable $compareFn = null ): Collection
Returns a new Collection without values found in $remove. The default comparison is strict identity. An optional comparator receives a value from this Collection and a value from $remove, and returns true when they should be considered equal.
$remaining = $cards->diff(
$playedCards,
fn(Card $a, Card $b): bool => $a->id === $b->id,
);
The original keys of retained values are preserved.
merge( Collection $collection ): Collection
Returns a new Collection containing the values from this Collection followed by keys that do not already exist from $collection. If both Collections contain the same key, the value from this Collection wins.
$left = new Collection([1 => 'one', 2 => 'left']); $right = new Collection([2 => 'right', 3 => 'three']); $left->merge($right)->all(); // [1 => 'one', 2 => 'left', 3 => 'three']
Transforming collections
pluck( string $property ): Collection
Returns a new Collection containing the named field from each item. Keys are preserved. Fields are resolved as an array key, a getter, or an object property.
map( callable $func ): Collection
Returns a new Collection containing $func($value, $key) for each value. Keys are preserved, but the returned values can have a different type.
reduce( callable $func, mixed $init ): mixed
Reduces the values to a single result. The callback receives the current accumulator and the next value. Collection keys are not passed.
$total = $cards->reduce(
fn(int $sum, Card $card): int => $sum + $card->value,
0,
);
filter( callable $func ): Collection
Returns a new Collection containing only the values for which $func($value, $key) is true. Keys are preserved.
slice( int $offset, ?int $length = null ): Collection
Returns a key-preserving slice starting at $offset. When $length is null, all remaining items are returned. Offset and length follow PHP's array_slice() rules.
take( int $n ): Collection
Returns a key-preserving Collection containing the first $n items.
sort( callable $callback ): Collection
Returns a new Collection sorted by a comparator compatible with PHP's uasort(). The comparator receives two values and returns an integer less than, equal to, or greater than zero. Keys are preserved.
Field-based helpers
where( string $field, mixed $value, bool $strict = true ): Collection
Returns the items whose named field matches $value. An array value is treated as an allowed set. A string containing % is treated as a case-insensitive wildcard pattern.
countWhere( string $field, mixed $value, bool $strict = true ): int
Counts the items whose named field matches $value, using the same matching rules as where().
whereNot( string $field, mixed $value, bool $strict = true ): Collection
Returns the items whose named field does not match $value, using the same matching rules as where().
whereNull( string $field ): Collection
Returns the items whose named field is null.
sortBy( string $field, string $direction = 'ASC' ): Collection
Returns a new Collection sorted by the named field. Pass 'DESC' for descending order; any other direction is treated as ascending. Keys are preserved.
groupBy( string|callable $criteria ): Collection
Groups items by a named field or by the result of $criteria($value, $key). It returns a Collection<Collection<T>>. Group keys must be integers or strings, and item keys are preserved inside each group.
update( string $field, mixed $value ): Collection
Returns a new Collection with the field set to $value on every item. Arrays are copied. Objects are cloned and updated through set<Field>() when available, or through a matching public property otherwise. The original items are left unchanged.