From 6a706f62ad866e7615731d0848ff634df4fdea82 Mon Sep 17 00:00:00 2001 From: Oluwatobi Ishola Date: Tue, 5 Aug 2025 19:20:06 +0100 Subject: [PATCH 1/3] Add containsAny() method to Collections- Add containsAny() method to Collection and LazyCollection classes- Add method signature to Enumerable interface- Support all contains() features: simple values, callbacks, key-value pairs, and operators- Add comprehensive test coverage for all usage patterns- Follow Laravel's existing patterns and conventions --- src/Illuminate/Collections/Collection.php | 33 ++++++++ src/Illuminate/Collections/Enumerable.php | 8 ++ src/Illuminate/Collections/LazyCollection.php | 31 ++++++++ tests/Support/SupportCollectionTest.php | 77 ++++++++++++++++++- 4 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index ce683bee94d1..fc020208b3ec 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -195,6 +195,39 @@ public function contains($key, $operator = null, $value = null) return $this->contains($this->operatorForWhere(...func_get_args())); } + /** + * Determine if any of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAny($values) + { + if ($this->isEmpty()) { + return false; + } + + $values = is_array($values) ? $values : func_get_args(); + + foreach ($values as $value) { + if ($this->useAsCallable($value)) { + $placeholder = new stdClass; + + if ($this->first($value, $placeholder) !== $placeholder) { + return true; + } + } elseif (is_array($value)) { + if ($this->contains(...$value)) { + return true; + } + } elseif (in_array($value, $this->items)) { + return true; + } + } + + return false; + } + /** * Determine if an item exists, using strict comparison. * diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 78187b785697..057eeb451257 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -153,6 +153,14 @@ public function avg($callback = null); */ public function contains($key, $operator = null, $value = null); + /** + * Determine if any of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAny($values); + /** * Determine if an item is not contained in the collection. * diff --git a/src/Illuminate/Collections/LazyCollection.php b/src/Illuminate/Collections/LazyCollection.php index 14665acd5312..b87455760fb0 100644 --- a/src/Illuminate/Collections/LazyCollection.php +++ b/src/Illuminate/Collections/LazyCollection.php @@ -248,6 +248,37 @@ public function contains($key, $operator = null, $value = null) return $this->contains($this->operatorForWhere(...func_get_args())); } + /** + * Determine if any of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAny($values) + { + if ($this->isEmpty()) { + return false; + } + + $values = is_array($values) ? $values : func_get_args(); + + foreach ($values as $value) { + if ($this->useAsCallable($value)) { + if ($this->first($value) !== null) { + return true; + } + } elseif (is_array($value)) { + if ($this->contains(...$value)) { + return true; + } + } elseif ($this->contains($value)) { + return true; + } + } + + return false; + } + /** * Determine if an item exists, using strict comparison. * diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 211c7be80641..4ab1663f4142 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -2414,10 +2414,12 @@ public function testImplode($collection) #[DataProvider('collectionClassProvider')] public function testImplodeModels($collection) { - $model = new class extends Model { + $model = new class extends Model + { }; $model->setAttribute('email', 'foo'); - $modelTwo = new class extends Model { + $modelTwo = new class extends Model + { }; $modelTwo->setAttribute('email', 'bar'); $data = new $collection([$model, $modelTwo]); @@ -3791,6 +3793,77 @@ public function testContainsWithOperator($collection) $this->assertTrue($c->contains('v', '>', 4)); } + #[DataProvider('collectionClassProvider')] + public function testContainsAny($collection) + { + $c = new $collection([1, 3, 5]); + + $this->assertTrue($c->containsAny([1, 2])); + $this->assertTrue($c->containsAny([2, 3])); + $this->assertFalse($c->containsAny([2, 4])); + $this->assertFalse($c->containsAny([])); + + $this->assertTrue($c->containsAny(1, 2)); + $this->assertTrue($c->containsAny(2, 3)); + $this->assertFalse($c->containsAny(2, 4)); + + $this->assertTrue($c->containsAny(['1', 2])); + $this->assertTrue($c->containsAny([2, '3'])); + + $c = new $collection([null, false, 0, '']); + $this->assertTrue($c->containsAny([null, 'missing'])); + $this->assertTrue($c->containsAny([false, 'missing'])); + $this->assertTrue($c->containsAny([0, 'missing'])); + $this->assertTrue($c->containsAny(['', 'missing'])); + $this->assertFalse($c->containsAny(['missing', 'also_missing'])); + + $c = new $collection([1, 3, 5]); + $this->assertTrue($c->containsAny([ + function ($value) { + return $value < 2; + }, + function ($value) { + return $value > 10; + }, + ])); + $this->assertFalse($c->containsAny([ + function ($value) { + return $value > 10; + }, + function ($value) { + return $value < 0; + }, + ])); + + $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); + $this->assertTrue($c->containsAny([['v', 1], ['v', 2]])); + $this->assertTrue($c->containsAny([['v', 2], ['v', 1]])); + $this->assertTrue($c->containsAny([['v', 2], ['v', 3]])); + $this->assertFalse($c->containsAny([['v', 2], ['v', 4]])); + + $c = new $collection([]); + $this->assertFalse($c->containsAny([1, 2, 3])); + $this->assertFalse($c->containsAny(1, 2, 3)); + } + + #[DataProvider('collectionClassProvider')] + public function testContainsAnyWithOperator($collection) + { + $c = new $collection([['v' => 1], ['v' => 3], ['v' => '4'], ['v' => 5]]); + + $this->assertTrue($c->containsAny([['v', '=', 4], ['v', '=', 6]])); + $this->assertTrue($c->containsAny([['v', '==', 4], ['v', '==', 6]])); + $this->assertFalse($c->containsAny([['v', '===', 4], ['v', '===', 6]])); + $this->assertTrue($c->containsAny([['v', '>', 4], ['v', '<', 0]])); + $this->assertTrue($c->containsAny([['v', '>', 0], ['v', '<', 0]])); + $this->assertFalse($c->containsAny([['v', '>', 10], ['v', '<', 0]])); + + $this->assertTrue($c->containsAny([ + ['v', '>', 10], + ['v', '=', 3], + ])); + } + #[DataProvider('collectionClassProvider')] public function testGettingSumFromCollection($collection) { From 66eedf2fc759c7777bfbeafcb5ba8ba6c74195bb Mon Sep 17 00:00:00 2001 From: Oluwatobi Ishola Date: Tue, 5 Aug 2025 21:00:15 +0100 Subject: [PATCH 2/3] Add containsAll() method to Collections - Add containsAll() method to Collection, LazyCollection, and Enumerable interface - Returns true if ALL of the given values exist in the collection - Supports all contains() features: simple values, callbacks, key-value pairs, and operators - Add comprehensive test coverage (34 assertions across 2 test methods) - Complements existing containsAny() method for complete coverage - Follow Laravel's existing patterns and conventions --- src/Illuminate/Collections/Collection.php | 37 ++++++++ src/Illuminate/Collections/Enumerable.php | 8 ++ src/Illuminate/Collections/LazyCollection.php | 35 ++++++++ tests/Support/SupportCollectionTest.php | 86 +++++++++++++++++++ 4 files changed, 166 insertions(+) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index fc020208b3ec..cb8ba17c1865 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -228,6 +228,43 @@ public function containsAny($values) return false; } + /** + * Determine if all of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAll($values) + { + $values = is_array($values) ? $values : func_get_args(); + + if (empty($values)) { + return true; + } + + if ($this->isEmpty()) { + return false; + } + + foreach ($values as $value) { + if ($this->useAsCallable($value)) { + $placeholder = new stdClass; + + if ($this->first($value, $placeholder) === $placeholder) { + return false; + } + } elseif (is_array($value)) { + if (!$this->contains(...$value)) { + return false; + } + } elseif (!in_array($value, $this->items)) { + return false; + } + } + + return true; + } + /** * Determine if an item exists, using strict comparison. * diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 057eeb451257..a22cebe2b1c2 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -161,6 +161,14 @@ public function contains($key, $operator = null, $value = null); */ public function containsAny($values); + /** + * Determine if all of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAll($values); + /** * Determine if an item is not contained in the collection. * diff --git a/src/Illuminate/Collections/LazyCollection.php b/src/Illuminate/Collections/LazyCollection.php index b87455760fb0..04a6f5523e05 100644 --- a/src/Illuminate/Collections/LazyCollection.php +++ b/src/Illuminate/Collections/LazyCollection.php @@ -279,6 +279,41 @@ public function containsAny($values) return false; } + /** + * Determine if all of the given items exist in the collection. + * + * @param mixed $values + * @return bool + */ + public function containsAll($values) + { + $values = is_array($values) ? $values : func_get_args(); + + if (empty($values)) { + return true; + } + + if ($this->isEmpty()) { + return false; + } + + foreach ($values as $value) { + if ($this->useAsCallable($value)) { + if ($this->first($value) === null) { + return false; + } + } elseif (is_array($value)) { + if (! $this->contains(...$value)) { + return false; + } + } elseif (! $this->contains($value)) { + return false; + } + } + + return true; + } + /** * Determine if an item exists, using strict comparison. * diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 4ab1663f4142..701b6e8c17c5 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -3864,6 +3864,92 @@ public function testContainsAnyWithOperator($collection) ])); } + #[DataProvider('collectionClassProvider')] + public function testContainsAll($collection) + { + $c = new $collection([1, 3, 5]); + + $this->assertTrue($c->containsAll([1, 3])); + $this->assertTrue($c->containsAll([3, 5])); + $this->assertTrue($c->containsAll([1, 3, 5])); + $this->assertFalse($c->containsAll([1, 2])); + $this->assertFalse($c->containsAll([2, 4])); + $this->assertTrue($c->containsAll([])); + + $this->assertTrue($c->containsAll(1, 3)); + $this->assertTrue($c->containsAll(3, 5)); + $this->assertFalse($c->containsAll(1, 2)); + + $this->assertTrue($c->containsAll(['1', 3])); + $this->assertTrue($c->containsAll([3, '5'])); + + $c = new $collection([null, false, 0, '']); + $this->assertTrue($c->containsAll([null, false])); + $this->assertTrue($c->containsAll([0, ''])); + $this->assertTrue($c->containsAll([null, false, 0, ''])); + $this->assertFalse($c->containsAll([null, 'missing'])); + $this->assertFalse($c->containsAll(['missing', 'also_missing'])); + + $c = new $collection([1, 3, 5]); + $this->assertTrue($c->containsAll([ + function ($value) { + return $value < 2; + }, + function ($value) { + return $value > 4; + }, + ])); + $this->assertFalse($c->containsAll([ + function ($value) { + return $value > 10; + }, + function ($value) { + return $value < 0; + }, + ])); + $this->assertFalse($c->containsAll([ + function ($value) { + return $value < 2; + }, + function ($value) { + return $value > 10; + }, + ])); + + $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); + $this->assertTrue($c->containsAll([['v', 1], ['v', 3]])); + $this->assertTrue($c->containsAll([['v', 3], ['v', 5]])); + $this->assertFalse($c->containsAll([['v', 1], ['v', 2]])); + $this->assertFalse($c->containsAll([['v', 2], ['v', 4]])); + + $c = new $collection([]); + $this->assertTrue($c->containsAll([])); + $this->assertFalse($c->containsAll([1])); + $this->assertFalse($c->containsAll(1, 2, 3)); + } + + #[DataProvider('collectionClassProvider')] + public function testContainsAllWithOperator($collection) + { + $c = new $collection([['v' => 1], ['v' => 3], ['v' => '4'], ['v' => 5]]); + + $this->assertTrue($c->containsAll([['v', '=', 4], ['v', '=', 3]])); + $this->assertTrue($c->containsAll([['v', '==', 4], ['v', '==', 1]])); + $this->assertFalse($c->containsAll([['v', '===', 4], ['v', '===', 3]])); + $this->assertTrue($c->containsAll([['v', '>', 0], ['v', '<', 10]])); + $this->assertTrue($c->containsAll([['v', '>', 4], ['v', '<', 2]])); + $this->assertFalse($c->containsAll([['v', '>', 10], ['v', '<', 0]])); + + $this->assertTrue($c->containsAll([ + ['v', '>', 0], + ['v', '=', 3], + ])); + $this->assertFalse($c->containsAll([ + ['v', '>', 0], + ['v', '=', 99], + ])); + } + #[DataProvider('collectionClassProvider')] public function testGettingSumFromCollection($collection) { From 8daae0b5df43ba9772e5fa9690c4f9a83f9a7b20 Mon Sep 17 00:00:00 2001 From: Oluwatobi Ishola Date: Tue, 5 Aug 2025 22:11:16 +0100 Subject: [PATCH 3/3] style: adjust code formatting and spacing in Collection class and test --- src/Illuminate/Collections/Collection.php | 4 ++-- tests/Support/SupportCollectionTest.php | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index cb8ba17c1865..bfb5cdd81adb 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -254,10 +254,10 @@ public function containsAll($values) return false; } } elseif (is_array($value)) { - if (!$this->contains(...$value)) { + if (! $this->contains(...$value)) { return false; } - } elseif (!in_array($value, $this->items)) { + } elseif (! in_array($value, $this->items)) { return false; } } diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 701b6e8c17c5..7f8e6a65983b 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -2414,12 +2414,10 @@ public function testImplode($collection) #[DataProvider('collectionClassProvider')] public function testImplodeModels($collection) { - $model = new class extends Model - { + $model = new class extends Model { }; $model->setAttribute('email', 'foo'); - $modelTwo = new class extends Model - { + $modelTwo = new class extends Model { }; $modelTwo->setAttribute('email', 'bar'); $data = new $collection([$model, $modelTwo]);