$item) { if ( $callback($item, $key, $idx) ) { $idx++; if ( $yieldValueOnly ) { yield $item; } else { yield $key => $item; } } } } public function filtersCollection(Callable $callback, bool $replaceCollection = false) : self { $collection = new static(); foreach($this->filters($callback, true) as $item) { $collection->append($item); } if ($replaceCollection) { $this->exchangeArray(array_values($collection->getArrayCopy())); return $this; } else { return $collection; } } public function iterate(Callable $callback) : self { foreach($this as $item) { $callback($item); } return $this; } public function removeOne($value, string $field, bool $strict = true) : ? object { foreach($this->search($value, $field, $strict) as $key => $item) { $this->offsetUnset($key); return $item; } return null; } public function remove($value, string $field, bool $strict = true) : array { $removed = []; foreach($this->search($value, $field, $strict) as $key => $item) { $this->offsetUnset($key); $removed[] = $item; } return $removed; } public function clear() : self { $this->exchangeArray([]); return $this; } public function search($value, string $field, bool $strict = true) : Generator { foreach($this->filters(fn($v) => $strict ? $v->$field === $value : $v->$field == $value) as $key => $item) { yield $key => $item; } } public function searchOne($value, string $field, bool $strict = true) : ? object { # Returning first value only foreach($this->search($value, $field, $strict) as $item) { return $item; } return null; } public function searchAll($value, string $field, bool $strict = true) : self { $obj = new static(); foreach($this->search($value, $field, $strict) as $item) { $obj->append($item); } return $obj; } public function searchInstances(string $className) : self { return $this->filtersCollection(fn($obj) => is_a($obj, $className)); } public function column($field, bool $unique = false) : array { $list = []; foreach($this as $item) { if ( is_callable($field) ) { $value = call_user_func_array($field, [ $item ]); } else { $value = $item->$field; } if ($unique && in_array($value, $list, true)) { continue; } $list[] = $value; } return $list; } public function unique(/*stringable|callable */ $field, bool $strict = false) : self { $list = []; $obj = new static(); foreach($this as $item) { if ( $field === null) { $value = $this; } if ( is_callable($field) ) { $value = call_user_func_array($field, [ $item ]); } else { $value = $item->$field; } if ( ! in_array($value, $list, $strict) ) { $list[] = $value; $obj->append($item); } } return $obj; } public function first() : ? object { foreach($this as $item) { return $item; } return null; } public function last() : ? object { foreach($this as $item) { $return = $item; } return $return ?? null; } public function buildArray(string $keyColumn, /* string|callable|null */ $value = null) : array { $list = []; foreach($this as $item) { switch (true) { case is_null($value): $list[] = $item->$keyColumn; break; case is_callable($value): $list[$item->$keyColumn] = $value($item); break; case is_object($value): case is_string($value): $value = (string) $value; $list[$item->$keyColumn] = $item->$value; break; } } return $list; } public function implode(string $glue, ? Callable $callback = null) : string { $values = []; foreach($this as $item) { $values[] = $callback ? $callback($item) : (string) $item; } return implode($glue, $values); } public function toArray(bool $includeRelations = false) : array { $list = []; foreach($this as $entity) { $list[] = $entity->toArray($includeRelations); } return $list; } public function fromArray(array $datasets, ? string /*stringable*/ $entityClass = null) : self { foreach($datasets as $dataset) { $this->append( $this->arrayToEntity($dataset, $entityClass) ); } return $this; } public function arrayToEntity(array $dataset, ? string /*stringable*/ $entityClass = null) : object { if ( ! ($this->entityClass || $entityClass) ) { throw new \Exception("An entity class name must be provided to be instanciated and populated before insertion into this collection."); } $className = $entityClass ?: $this->entityClass; return ( new $className() )->fromArray($dataset); } public function append($value) : void { if ( is_array($value) ) { $this->append( $this->arrayToEntity($value) ); } else { parent::append($value); } } public function mergeWith(... $datasets) : self { $list = []; foreach($datasets as $dataset) { if ( is_object($dataset) ) { $list = array_merge($dataset->getArrayCopy(), $list); } else { $list = array_merge($dataset, $list); } } $this->exchangeArray( array_merge( $this->getArrayCopy(), $list ) ); return $this; } public function replaceWith( /*array|EntityCollection*/ $datasets ) : self { if ( is_object($datasets) ) { $datasets = $datasets->getArrayCopy(); } $this->exchangeArray( $datasets ); return $this; } public function randomize() : self { $arr = $this->getArrayCopy(); shuffle($arr); $this->exchangeArray($arr); return $this; } public function sort(callable $callback, $function = "uasort") : self { call_user_func_array([ $this, $function ], [ $callback ]); return $this; } public function rsort(callable $callback, $function = "uasort") : self { $this->sort(...func_get_args()); return $this->reverse(); } public function reverse() : self { return $this->replaceWith(array_reverse($this->getArrayCopy()));; } public function slice(int $offset, ? int $length = null) : self { return new self(array_slice($this->getArrayCopy(), $offset, $length)); } }