From 1f7d851738feb847f275f0780a6aca16ff868cd7 Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 15:08:40 +0100 Subject: [PATCH 1/7] feat: add fromIterable function for Collection --- src/Domain/Collection.php | 27 +++++++++++++++++++++++++-- tests/Domain/CollectionTest.php | 20 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Domain/Collection.php b/src/Domain/Collection.php index d8e2b34..91728da 100644 --- a/src/Domain/Collection.php +++ b/src/Domain/Collection.php @@ -5,6 +5,8 @@ namespace GeekCell\Ddd\Domain; use Assert; +use InvalidArgumentException; +use Traversable; /** * @template T of object @@ -26,11 +28,32 @@ final public function __construct( } } + /** + * Creates a collection from a given iterable of items. + * This function is useful when trying to create a collection from a generator or an iterator + * + * @param iterable $items + * @param class-string|null $itemType + * @return self + */ + public static function fromIterable(iterable $items, ?string $itemType = null): static + { + if (is_array($items)) { + return new static($items, $itemType); + } + + if (!$items instanceof Traversable) { + $items = [...$items]; + } + + return new static(iterator_to_array($items), $itemType); + } + /** * Add one or more items to the collection. It **does not** modify the * current collection, but returns a new one. * - * @param mixed $item One or more items to add to the collection. + * @param T|iterable $item One or more items to add to the collection. * @return static */ public function add(mixed $item): static @@ -158,7 +181,7 @@ public function count(): int /** * @inheritDoc */ - public function getIterator(): \Traversable + public function getIterator(): Traversable { return new \ArrayIterator($this->items); } diff --git a/tests/Domain/CollectionTest.php b/tests/Domain/CollectionTest.php index 86044d5..6f512c8 100644 --- a/tests/Domain/CollectionTest.php +++ b/tests/Domain/CollectionTest.php @@ -4,6 +4,7 @@ namespace GeekCell\Ddd\Tests\Domain; +use ArrayIterator; use Assert; use GeekCell\Ddd\Domain\Collection; use PHPUnit\Framework\TestCase; @@ -300,4 +301,23 @@ public function testChaining(): void // Then $this->assertEquals(60, $result); } + + public function testFromIterable(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collectionFromArray = Collection::fromIterable($items); + $this->assertSame($items, iterator_to_array($collectionFromArray)); + + $collectionFromIterator = Collection::fromIterable(new ArrayIterator($items)); + $this->assertSame($items, iterator_to_array($collectionFromIterator)); + + $generatorFn = static function () use ($items) { + foreach ($items as $item) { + yield $item; + } + }; + + $collectionFromGenerator = Collection::fromIterable($generatorFn()); + $this->assertSame($items, iterator_to_array($collectionFromGenerator)); + } } From cb1fe18f87d383fa9ba3b457810bfb410319d815 Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 15:53:31 +0100 Subject: [PATCH 2/7] feat: add every, none and some functions to collection --- src/Domain/Collection.php | 70 ++++++++++++++++++++ tests/Domain/CollectionTest.php | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/src/Domain/Collection.php b/src/Domain/Collection.php index 91728da..82f368f 100644 --- a/src/Domain/Collection.php +++ b/src/Domain/Collection.php @@ -35,6 +35,7 @@ final public function __construct( * @param iterable $items * @param class-string|null $itemType * @return self + * @throws Assert\AssertionFailedException */ public static function fromIterable(iterable $items, ?string $itemType = null): static { @@ -49,6 +50,75 @@ public static function fromIterable(iterable $items, ?string $itemType = null): return new static(iterator_to_array($items), $itemType); } + /** + * Returns true if every value in the collection passes the callback truthy test. Opposite of self::none(). + * Callback arguments will be element, index, collection. + * Function short-circuits on first falsy return value + * + * @param ?callable(T, int, static): bool $callback + * @return bool + */ + public function every(callable $callback = null): bool + { + if ($callback === null) { + $callback = static fn ($item, $index, $self) => $item; + } + + foreach ($this->items as $index => $item) { + if (!$callback($item, $index, $this)) { + return false; + } + } + + return true; + } + + /** + * Returns true if every value in the collection passes the callback falsy test. Opposite of self::every(). + * Callback arguments will be element, index, collection. + * Function short-circuits on first truthy return value + * + * @param ?callable(T, int, static): bool $callback + * @return bool + */ + public function none(callable $callback = null): bool + { + if ($callback === null) { + $callback = static fn ($item, $index, $self) => $item; + } + + foreach ($this->items as $index => $item) { + if ($callback($item, $index, $this)) { + return false; + } + } + + return true; + } + + /** + * Returns true if at least one value in the collection passes the callback truthy test. + * Callback arguments will be element, index, collection. + * Function short-circuits on first truthy return value + * + * @param ?callable(T, int, static): bool $callback + * @return bool + */ + public function some(callable $callback = null): bool + { + if ($callback === null) { + $callback = static fn ($item, $index, $self) => $item; + } + + foreach ($this->items as $index => $item) { + if ($callback($item, $index, $this)) { + return true; + } + } + + return false; + } + /** * Add one or more items to the collection. It **does not** modify the * current collection, but returns a new one. diff --git a/tests/Domain/CollectionTest.php b/tests/Domain/CollectionTest.php index 6f512c8..b154684 100644 --- a/tests/Domain/CollectionTest.php +++ b/tests/Domain/CollectionTest.php @@ -320,4 +320,115 @@ public function testFromIterable(): void $collectionFromGenerator = Collection::fromIterable($generatorFn()); $this->assertSame($items, iterator_to_array($collectionFromGenerator)); } + + public function testEvery(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $this->assertFalse($collection->every(static fn ($item) => $item > 10)); + $this->assertFalse($collection->every(static fn ($item) => $item > 5)); + $this->assertTrue($collection->every(static fn ($item) => $item > 0)); + } + + public function testEveryWithoutArgumentDefaultsToTruthyCheck(): void + { + $this->assertTrue((new Collection([1, true]))->every()); + $this->assertTrue((new Collection([1, true]))->every()); + $this->assertFalse((new Collection([null, false]))->every()); + $this->assertFalse((new Collection([false, null]))->every()); + $this->assertFalse((new Collection([0, false]))->every()); + } + + public function testEveryReturnsTrueOnEmptyCollection(): void + { + $this->assertTrue((new Collection())->every(static fn ($item) => false)); + } + + public function testEveryShortCircuitsOnFirstFalsyValue(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $collection->every(function ($item, $index, $c) use ($collection): bool { + // First item already returns false therefore the index should never be something other than 0 + $this->assertSame(0, $index); + $this->assertSame($c, $collection); + return false; + }); + } + + public function testNone(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $this->assertTrue($collection->none(static fn ($item) => $item > 10)); + $this->assertFalse($collection->none(static fn ($item) => $item > 5)); + $this->assertFalse($collection->none(static fn ($item) => $item > 0)); + } + + public function testNoneWithoutArgumentDefaultsToTruthyCheck(): void + { + $this->assertFalse((new Collection([1, true]))->none()); + $this->assertFalse((new Collection([1, true]))->none()); + $this->assertTrue((new Collection([null, false]))->none()); + $this->assertTrue((new Collection([false, null]))->none()); + $this->assertTrue((new Collection([0, false]))->none()); + } + + public function testNoneReturnsFalseOnEmptyCollection(): void + { + $this->assertTrue((new Collection())->none(static fn ($item) => true)); + } + + public function testNoneShortCircuitsOnFirstFalsyValue(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $collection->none(function ($item, $index, $c) use ($collection): bool { + // First item already returns true therefore the index should never be something other than 0 + $this->assertSame(0, $index); + $this->assertSame($c, $collection); + return true; + }); + } + + public function testSome(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $this->assertFalse($collection->some(static fn ($item) => $item > 10)); + $this->assertTrue($collection->some(static fn ($item) => $item > 5)); + $this->assertTrue($collection->some(static fn ($item) => $item > 0)); + } + + public function testSomeWithoutArgumentDefaultsToTruthyCheck(): void + { + $this->assertTrue((new Collection([1, true]))->some()); + $this->assertTrue((new Collection([1, true]))->some()); + $this->assertFalse((new Collection([null, false]))->some()); + $this->assertFalse((new Collection([false, null]))->some()); + $this->assertFalse((new Collection([0, false]))->some()); + } + + public function testSomeReturnsFalseOnEmptyCollection(): void + { + $this->assertFalse((new Collection())->some(static fn ($item) => true)); + } + + public function testSomeShortCircuitsOnFirstFalsyValue(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $collection->some(function ($item, $index, $c) use ($collection): bool { + // First item already returns true therefore the index should never be something other than 0 + $this->assertSame(0, $index); + $this->assertSame($c, $collection); + return true; + }); + } } From 74ce180a23efc4989d5b019191b241a4bdbf4174 Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 16:52:15 +0100 Subject: [PATCH 3/7] feat: add first, firstOr, last, lastOr functions to collection --- src/Domain/Collection.php | 108 ++++++++++++++++++++++++++++++-- tests/Domain/CollectionTest.php | 95 ++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 4 deletions(-) diff --git a/src/Domain/Collection.php b/src/Domain/Collection.php index 82f368f..280f916 100644 --- a/src/Domain/Collection.php +++ b/src/Domain/Collection.php @@ -30,7 +30,7 @@ final public function __construct( /** * Creates a collection from a given iterable of items. - * This function is useful when trying to create a collection from a generator or an iterator + * This function is useful when trying to create a collection from a generator or an iterator. * * @param iterable $items * @param class-string|null $itemType @@ -53,7 +53,7 @@ public static function fromIterable(iterable $items, ?string $itemType = null): /** * Returns true if every value in the collection passes the callback truthy test. Opposite of self::none(). * Callback arguments will be element, index, collection. - * Function short-circuits on first falsy return value + * Function short-circuits on first falsy return value. * * @param ?callable(T, int, static): bool $callback * @return bool @@ -76,7 +76,7 @@ public function every(callable $callback = null): bool /** * Returns true if every value in the collection passes the callback falsy test. Opposite of self::every(). * Callback arguments will be element, index, collection. - * Function short-circuits on first truthy return value + * Function short-circuits on first truthy return value. * * @param ?callable(T, int, static): bool $callback * @return bool @@ -99,7 +99,7 @@ public function none(callable $callback = null): bool /** * Returns true if at least one value in the collection passes the callback truthy test. * Callback arguments will be element, index, collection. - * Function short-circuits on first truthy return value + * Function short-circuits on first truthy return value. * * @param ?callable(T, int, static): bool $callback * @return bool @@ -119,6 +119,106 @@ public function some(callable $callback = null): bool return false; } + /** + * Returns the first element of the collection that matches the given callback. + * If no callback is given the first element in the collection is returned. + * Throws exception if collection is empty or the given callback was never satisfied. + * + * @param ?callable(T, int, static): bool $callback + * @return T + * @throws InvalidArgumentException + */ + public function first(callable $callback = null) + { + if ($this->items === []) { + throw new InvalidArgumentException('No items in collection'); + } + + foreach ($this->items as $index => $item) { + if ($callback === null || $callback($item, $index, $this)) { + return $item; + } + } + + throw new InvalidArgumentException('No item found in collection that satisfies first callback'); + } + + /** + * Returns the first element of the collection that matches the given callback. + * If no callback is given the first element in the collection is returned. + * If the collection is empty the given fallback value is returned instead. + * + * @template U of T|mixed + * @param ?callable(T, int, static): bool $callback + * @param U $fallbackValue + * @return U + * @throws InvalidArgumentException + */ + public function firstOr(callable $callback = null, mixed $fallbackValue = null) + { + if ($this->items === []) { + return $fallbackValue; + } + + foreach ($this->items as $index => $item) { + if ($callback === null || $callback($item, $index, $this)) { + return $item; + } + } + + return $fallbackValue; + } + + /** + * Returns the last element of the collection that matches the given callback. + * If no callback is given the last element in the collection is returned. + * Throws exception if collection is empty or the given callback was never satisfied. + * + * @param ?callable(T, int, static): bool $callback + * @return T + * @throws InvalidArgumentException + */ + public function last(callable $callback = null) + { + if ($this->items === []) { + throw new InvalidArgumentException('No items in collection'); + } + + foreach (array_reverse($this->items) as $index => $item) { + if ($callback === null || $callback($item, $index, $this)) { + return $item; + } + } + + throw new InvalidArgumentException('No item found in collection that satisfies last callback'); + } + + /** + * Returns the last element of the collection that matches the given callback. + * If no callback is given the last element in the collection is returned. + * If the collection is empty the given fallback value is returned instead. + * + * @template U of T|mixed + * @param ?callable(T, int, static): bool $callback + * @param U $fallbackValue + * @return U + * @throws InvalidArgumentException + */ + public function lastOr(callable $callback = null, mixed $fallbackValue = null) + { + if ($this->items === []) { + return $fallbackValue; + } + + foreach (array_reverse($this->items) as $index => $item) { + if ($callback === null || $callback($item, $index, $this)) { + return $item; + } + } + + return $fallbackValue; + } + /** * Add one or more items to the collection. It **does not** modify the * current collection, but returns a new one. diff --git a/tests/Domain/CollectionTest.php b/tests/Domain/CollectionTest.php index b154684..0972163 100644 --- a/tests/Domain/CollectionTest.php +++ b/tests/Domain/CollectionTest.php @@ -7,6 +7,7 @@ use ArrayIterator; use Assert; use GeekCell\Ddd\Domain\Collection; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; /** @@ -431,4 +432,98 @@ public function testSomeShortCircuitsOnFirstFalsyValue(): void return true; }); } + + public function testFirst(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $this->assertSame(1, $collection->first()); + } + + public function testFirstThrowsExceptionOnEmptyCollection(): void + { + $collection = new Collection([]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No items in collection'); + $collection->first(); + } + + public function testFirstThrowsExceptionIfCallbackIsNeverSatisfied(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No item found in collection that satisfies first callback'); + $collection->first(static fn () => false); + } + + public function testFirstOrReturnsFirstValueInCollectionIfNoCallbackIsGiven(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(1, $collection->firstOr()); + } + + public function testFirstOrReturnsFirstValueThatSatisfiesCallback(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(6, $collection->firstOr(static fn ($item) => $item > 5)); + } + + public function testFirstOrReturnsFallbackValueIfCallbackIsNeverSatisfied(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(-1, $collection->firstOr(static fn ($item) => $item > 10, -1)); + } + + public function testLast(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + + $this->assertSame(10, $collection->last()); + } + + public function testLastThrowsExceptionOnEmptyCollection(): void + { + $collection = new Collection([]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No items in collection'); + $collection->last(); + } + + public function testLastThrowsExceptionIfCallbackIsNeverSatisfied(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No item found in collection that satisfies last callback'); + $collection->last(static fn () => false); + } + + public function testLastOrReturnsLastValueInCollectionIfNoCallbackIsGiven(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(10, $collection->lastOr()); + } + + public function testLastOrReturnsLastValueThatSatisfiesCallback(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(10, $collection->lastOr(static fn ($item) => $item > 5)); + } + + public function testLastOrReturnsFallbackValueIfCallbackIsNeverSatisfied(): void + { + $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + $collection = new Collection($items); + $this->assertSame(-1, $collection->lastOr(static fn ($item) => $item > 10, -1)); + } } From 52ced3c98c6f334b4f9ecd1ea3c933efdb1ea966 Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 17:20:07 +0100 Subject: [PATCH 4/7] refac: import functions in Collection --- src/Domain/Collection.php | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Domain/Collection.php b/src/Domain/Collection.php index 280f916..4f89d82 100644 --- a/src/Domain/Collection.php +++ b/src/Domain/Collection.php @@ -4,16 +4,28 @@ namespace GeekCell\Ddd\Domain; +use ArrayAccess; +use ArrayIterator; use Assert; +use Countable; use InvalidArgumentException; +use IteratorAggregate; use Traversable; +use function array_filter; +use function array_map; +use function array_reduce; +use function array_values; +use function count; +use function get_class; +use function is_int; +use function reset; /** * @template T of object - * @implements \IteratorAggregate - * @implements \ArrayAccess + * @implements IteratorAggregate + * @implements ArrayAccess */ -class Collection implements \ArrayAccess, \Countable, \IteratorAggregate +class Collection implements ArrayAccess, Countable, IteratorAggregate { /** * @param T[] $items @@ -249,7 +261,7 @@ public function add(mixed $item): static public function filter(callable $callback): static { return new static( - \array_values(\array_filter($this->items, $callback)), + array_values(array_filter($this->items, $callback)), $this->itemType, ); } @@ -267,15 +279,15 @@ public function filter(callable $callback): static */ public function map(callable $callback, bool $inferTypes = true): static { - $mapResult = \array_map($callback, $this->items); - $firstItem = \reset($mapResult); + $mapResult = array_map($callback, $this->items); + $firstItem = reset($mapResult); if ($firstItem === false || !is_object($firstItem)) { return new static($mapResult); } if ($inferTypes && $this->itemType !== null) { - return new static($mapResult, \get_class($firstItem)); + return new static($mapResult, get_class($firstItem)); } return new static($mapResult); @@ -291,7 +303,7 @@ public function map(callable $callback, bool $inferTypes = true): static */ public function reduce(callable $callback, mixed $initial = null): mixed { - return \array_reduce($this->items, $callback, $initial); + return array_reduce($this->items, $callback, $initial); } /** @@ -299,7 +311,7 @@ public function reduce(callable $callback, mixed $initial = null): mixed */ public function offsetExists(mixed $offset): bool { - if (!\is_int($offset)) { + if (!is_int($offset)) { return false; } @@ -345,7 +357,7 @@ public function offsetUnset(mixed $offset): void */ public function count(): int { - return \count($this->items); + return count($this->items); } /** @@ -353,6 +365,6 @@ public function count(): int */ public function getIterator(): Traversable { - return new \ArrayIterator($this->items); + return new ArrayIterator($this->items); } } From 63d83eefe102884c829bd88f1b718c0f2edf589e Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 17:33:15 +0100 Subject: [PATCH 5/7] docs: add changelog for new features --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3658e7a..21dc6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [1.4.1](https://github.com/geekcell/php-ddd/compare/v1.4.0...v1.4.1) (2024-01-05) + +### Features + +Added a couple of functions to the [Collection](./src/Domain/Collection.php) class to enable a smoother developer experience when trying use it in a more functional style + +The following functions were added: +* `fromIterable` - enables users of the collection to construction the collection from an iterable value like iterators, generators, etc. +* `every` - Function that returns true if given callback returns truthy values for all items +* `none` - Function that returns true if given callback returns falsy values for all items +* `some` - Function that returns true if given callback returns truthy values on some items +* `first` - Get the first element of the collection that matches a callback, if given. Throws exception if collection is empty or predicate is never satisfied +* `firstOr` - Same as first but returns $fallbackValue if collection is empty or predicate is never satisfied +* `last` - Get the last element of the collection that matches a callback, if given. Throws exception if collection is empty or predicate is never satisfied +* `lastOr` - Same as last but returns $fallbackValue if collection is empty or predicate is never satisfied + ## [1.4.0](https://github.com/geekcell/php-ddd/compare/v1.3.1...v1.4.0) (2023-12-19) From 1d6b0b823d8fddd4a3e53137225cf55b11a75c1f Mon Sep 17 00:00:00 2001 From: Bl00D4NGEL Date: Fri, 5 Jan 2024 17:54:39 +0100 Subject: [PATCH 6/7] feat: add isEmpty and hasItems to collection --- CHANGELOG.md | 8 +++++--- src/Domain/Collection.php | 16 ++++++++++++++++ tests/Domain/CollectionTest.php | 12 ++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21dc6e9..1240188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,15 @@ Added a couple of functions to the [Collection](./src/Domain/Collection.php) cla The following functions were added: * `fromIterable` - enables users of the collection to construction the collection from an iterable value like iterators, generators, etc. -* `every` - Function that returns true if given callback returns truthy values for all items -* `none` - Function that returns true if given callback returns falsy values for all items -* `some` - Function that returns true if given callback returns truthy values on some items +* `every` - Returns true if given callback returns truthy values for all items +* `none` - Returns true if given callback returns falsy values for all items +* `some` - Returns true if given callback returns truthy values on some items * `first` - Get the first element of the collection that matches a callback, if given. Throws exception if collection is empty or predicate is never satisfied * `firstOr` - Same as first but returns $fallbackValue if collection is empty or predicate is never satisfied * `last` - Get the last element of the collection that matches a callback, if given. Throws exception if collection is empty or predicate is never satisfied * `lastOr` - Same as last but returns $fallbackValue if collection is empty or predicate is never satisfied +* `isEmpty` - Returns whether the collection is empty +* `hasItems` - Returns whether the collection has items ## [1.4.0](https://github.com/geekcell/php-ddd/compare/v1.3.1...v1.4.0) (2023-12-19) diff --git a/src/Domain/Collection.php b/src/Domain/Collection.php index 4f89d82..f1d99e2 100644 --- a/src/Domain/Collection.php +++ b/src/Domain/Collection.php @@ -231,6 +231,22 @@ public function lastOr(callable $callback = null, mixed $fallbackValue = null) return $fallbackValue; } + /** + * Returns whether the collection is empty (has no items) + */ + public function isEmpty(): bool + { + return $this->items === []; + } + + /** + * Returns whether the collection has items + */ + public function hasItems(): bool + { + return $this->items !== []; + } + /** * Add one or more items to the collection. It **does not** modify the * current collection, but returns a new one. diff --git a/tests/Domain/CollectionTest.php b/tests/Domain/CollectionTest.php index 0972163..ded3457 100644 --- a/tests/Domain/CollectionTest.php +++ b/tests/Domain/CollectionTest.php @@ -526,4 +526,16 @@ public function testLastOrReturnsFallbackValueIfCallbackIsNeverSatisfied(): void $collection = new Collection($items); $this->assertSame(-1, $collection->lastOr(static fn ($item) => $item > 10, -1)); } + + public function testIsEmpty(): void + { + $this->assertFalse((new Collection([1]))->isEmpty()); + $this->assertTrue((new Collection([]))->isEmpty()); + } + + public function testHasItems(): void + { + $this->assertFalse((new Collection([]))->hasItems()); + $this->assertTrue((new Collection([1]))->hasItems()); + } } From 069a6000751c88b72271e4d0390815c9328f900a Mon Sep 17 00:00:00 2001 From: janvt Date: Mon, 8 Jan 2024 11:16:40 +0100 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1240188..77194e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [1.4.1](https://github.com/geekcell/php-ddd/compare/v1.4.0...v1.4.1) (2024-01-05) +## [1.5.0](https://github.com/geekcell/php-ddd/compare/v1.4.0...v1.5.0) (2024-01-05) ### Features