From 678ee93afb454de73fa4022362c94a8122863e32 Mon Sep 17 00:00:00 2001 From: Cristian Calara Date: Mon, 9 Oct 2023 17:49:26 +0300 Subject: [PATCH] [10.x] Implement chunkById in descending order (#48666) * Implement chunkById in descending order * formatting --------- Co-authored-by: Taylor Otwell --- .../Database/Concerns/BuildsQueries.php | 35 ++++++++++++++++++- tests/Database/DatabaseQueryBuilderTest.php | 20 +++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Concerns/BuildsQueries.php b/src/Illuminate/Database/Concerns/BuildsQueries.php index df1f40ce08ed..0d45b0a6f6fc 100644 --- a/src/Illuminate/Database/Concerns/BuildsQueries.php +++ b/src/Illuminate/Database/Concerns/BuildsQueries.php @@ -112,6 +112,35 @@ public function each(callable $callback, $count = 1000) * @return bool */ public function chunkById($count, callable $callback, $column = null, $alias = null) + { + return $this->orderedChunkById($count, $callback, $column, $alias); + } + + /** + * Chunk the results of a query by comparing IDs in descending order. + * + * @param int $count + * @param callable $callback + * @param string|null $column + * @param string|null $alias + * @return bool + */ + public function chunkByIdDesc($count, callable $callback, $column = null, $alias = null) + { + return $this->orderedChunkById($count, $callback, $column, $alias, descending: true); + } + + /** + * Chunk the results of a query by comparing IDs in a given order. + * + * @param int $count + * @param callable $callback + * @param string|null $column + * @param string|null $alias + * @param bool $descending + * @return bool + */ + public function orderedChunkById($count, callable $callback, $column = null, $alias = null, $descending = false) { $column ??= $this->defaultKeyName(); @@ -127,7 +156,11 @@ public function chunkById($count, callable $callback, $column = null, $alias = n // We'll execute the query for the given page and get the results. If there are // no results we can just break and return from here. When there are results // we will call the callback with the current chunk of these results here. - $results = $clone->forPageAfterId($count, $lastId, $column)->get(); + if ($descending) { + $results = $clone->forPageBeforeId($count, $lastId, $column)->get(); + } else { + $results = $clone->forPageAfterId($count, $lastId, $column)->get(); + } $countResults = $results->count(); diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 53dc6b40e565..b05798eca8a7 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -4421,6 +4421,26 @@ public function testChunkPaginatesUsingIdWithAlias() }, 'table.id', 'table_id'); } + public function testChunkPaginatesUsingIdDesc() + { + $builder = $this->getMockQueryBuilder(); + $builder->orders[] = ['column' => 'foobar', 'direction' => 'desc']; + + $chunk1 = collect([(object) ['someIdField' => 10], (object) ['someIdField' => 1]]); + $chunk2 = collect([]); + $builder->shouldReceive('forPageBeforeId')->once()->with(2, 0, 'someIdField')->andReturnSelf(); + $builder->shouldReceive('forPageBeforeId')->once()->with(2, 1, 'someIdField')->andReturnSelf(); + $builder->shouldReceive('get')->times(2)->andReturn($chunk1, $chunk2); + + $callbackAssertor = m::mock(stdClass::class); + $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk1); + $callbackAssertor->shouldReceive('doSomething')->never()->with($chunk2); + + $builder->chunkByIdDesc(2, function ($results) use ($callbackAssertor) { + $callbackAssertor->doSomething($results); + }, 'someIdField'); + } + public function testPaginate() { $perPage = 16;