From 719356038a5c9e3257e0b2db948aa132a75147fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 Feb 2026 16:05:09 +0100 Subject: [PATCH 1/5] Support generator for foreEach --- src/Database/Database.php | 8 +++++--- tests/e2e/Adapter/Scopes/DocumentTests.php | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 8e1b60556..d16f81ded 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7863,10 +7863,10 @@ public function find(string $collection, array $queries = [], string $forPermiss * @param callable $callback * @param array $queries * @param string $forPermission - * @return void + * @return \Generator * @throws \Utopia\Database\Exception */ - public function foreach(string $collection, callable $callback, array $queries = [], string $forPermission = Database::PERMISSION_READ): void + public function foreach(string $collection, ?callable $callback = null, array $queries = [], string $forPermission = Database::PERMISSION_READ): \Generator { $grouped = Query::groupByType($queries); $limitExists = $grouped['limit'] !== null; @@ -7906,8 +7906,10 @@ public function foreach(string $collection, callable $callback, array $queries = $sum = count($results); foreach ($results as $document) { - if (is_callable($callback)) { + if (!is_null($callback) && is_callable($callback)) { $callback($document); + } else { + yield $document; } } diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 868ffc84c..86ac55f75 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -4014,6 +4014,26 @@ public function testForeach(): void /** @var Database $database */ $database = $this->getDatabase(); + /** + * Test, foreach without callback on empty collection + */ + $database->createCollection('moviesEmpty'); + $documents = []; + foreach ($database->foreach('movies', queries: [Query::limit(2)], ) as $document) { + $documents[] = $document; + } + $this->assertEquals(0, \count($documents)); + $this->assertTrue($database->deleteCollection('moviesEmpty')); + + /** + * Test, foreach without callback + */ + $documents = []; + foreach ($database->foreach('movies', queries: [Query::limit(2)], ) as $document) { + $documents[] = $document; + } + $this->assertEquals(6, count($documents)); + /** * Test, foreach goes through all the documents */ From c5089739ae3c0cc5bfdf8c8ed60b0fc9edc72475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 Feb 2026 16:11:11 +0100 Subject: [PATCH 2/5] Fix tests --- tests/e2e/Adapter/Scopes/DocumentTests.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 86ac55f75..3c6cad9c9 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -4019,7 +4019,7 @@ public function testForeach(): void */ $database->createCollection('moviesEmpty'); $documents = []; - foreach ($database->foreach('movies', queries: [Query::limit(2)], ) as $document) { + foreach ($database->foreach('moviesEmpty', queries: [Query::limit(2)], ) as $document) { $documents[] = $document; } $this->assertEquals(0, \count($documents)); From 9596d781bfdc636e42c8485416a8aa7de6735f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 Feb 2026 16:18:02 +0100 Subject: [PATCH 3/5] PR review fixes --- tests/e2e/Adapter/Scopes/DocumentTests.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 3c6cad9c9..36428aa2f 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -4019,7 +4019,7 @@ public function testForeach(): void */ $database->createCollection('moviesEmpty'); $documents = []; - foreach ($database->foreach('moviesEmpty', queries: [Query::limit(2)], ) as $document) { + foreach ($database->foreach('moviesEmpty', queries: [Query::limit(2)]) as $document) { $documents[] = $document; } $this->assertEquals(0, \count($documents)); @@ -4029,7 +4029,7 @@ public function testForeach(): void * Test, foreach without callback */ $documents = []; - foreach ($database->foreach('movies', queries: [Query::limit(2)], ) as $document) { + foreach ($database->foreach('movies', queries: [Query::limit(2)]) as $document) { $documents[] = $document; } $this->assertEquals(6, count($documents)); From 35a146e14f0011584224febf3346edea687e2752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 Feb 2026 16:45:41 +0100 Subject: [PATCH 4/5] Fix bug --- src/Database/Database.php | 26 +++++++++++++++++----- tests/e2e/Adapter/Scopes/DocumentTests.php | 8 +++---- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index d16f81ded..9e37e6df5 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7855,6 +7855,24 @@ public function find(string $collection, array $queries = [], string $forPermiss return $results; } + /** + * Helper method to iterate documents in collection using callback pattern + * Alterative is + * + * @param string $collection + * @param callable $callback + * @param array $queries + * @param string $forPermission + * @return void + * @throws \Utopia\Database\Exception + */ + public function foreach(string $collection, callable $callback, array $queries = [], string $forPermission = Database::PERMISSION_READ): void + { + foreach ($this->iterate($collection, $queries, $forPermission) as $document) { + $callback($document); + } + } + /** * Call callback for each document of the given collection * that matches the given queries @@ -7866,7 +7884,7 @@ public function find(string $collection, array $queries = [], string $forPermiss * @return \Generator * @throws \Utopia\Database\Exception */ - public function foreach(string $collection, ?callable $callback = null, array $queries = [], string $forPermission = Database::PERMISSION_READ): \Generator + public function iterate(string $collection, array $queries = [], string $forPermission = Database::PERMISSION_READ): \Generator { $grouped = Query::groupByType($queries); $limitExists = $grouped['limit'] !== null; @@ -7906,11 +7924,7 @@ public function foreach(string $collection, ?callable $callback = null, array $q $sum = count($results); foreach ($results as $document) { - if (!is_null($callback) && is_callable($callback)) { - $callback($document); - } else { - yield $document; - } + yield $document; } $latestDocument = $results[array_key_last($results)]; diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 36428aa2f..f3d9d937e 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -4015,21 +4015,21 @@ public function testForeach(): void $database = $this->getDatabase(); /** - * Test, foreach without callback on empty collection + * Test, foreach generator on empty collection */ $database->createCollection('moviesEmpty'); $documents = []; - foreach ($database->foreach('moviesEmpty', queries: [Query::limit(2)]) as $document) { + foreach ($database->iterate('moviesEmpty', queries: [Query::limit(2)]) as $document) { $documents[] = $document; } $this->assertEquals(0, \count($documents)); $this->assertTrue($database->deleteCollection('moviesEmpty')); /** - * Test, foreach without callback + * Test, foreach generator */ $documents = []; - foreach ($database->foreach('movies', queries: [Query::limit(2)]) as $document) { + foreach ($database->iterate('movies', queries: [Query::limit(2)]) as $document) { $documents[] = $document; } $this->assertEquals(6, count($documents)); From 502f7ab0aa49483f50cc3859e8bf98b1c681aecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 10 Feb 2026 16:51:44 +0100 Subject: [PATCH 5/5] Fix linter --- src/Database/Database.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 9e37e6df5..c97b827e4 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -7874,11 +7874,10 @@ public function foreach(string $collection, callable $callback, array $queries = } /** - * Call callback for each document of the given collection + * Return each document of the given collection * that matches the given queries * * @param string $collection - * @param callable $callback * @param array $queries * @param string $forPermission * @return \Generator