diff --git a/src/Illuminate/Database/Concerns/BuildsQueries.php b/src/Illuminate/Database/Concerns/BuildsQueries.php index 54b18ed859e8..09c40027fc64 100644 --- a/src/Illuminate/Database/Concerns/BuildsQueries.php +++ b/src/Illuminate/Database/Concerns/BuildsQueries.php @@ -66,6 +66,72 @@ public function each(callable $callback, $count = 1000) }); } + /** + * Chunk the results of a query by comparing IDs. + * + * @param int $count + * @param callable $callback + * @param string|null $column + * @param string|null $alias + * @return bool + */ + public function chunkById($count, callable $callback, $column = null, $alias = null) + { + $column = $column ?? $this->defaultKeyName(); + + $alias = $alias ?? $column; + + $lastId = null; + + do { + $clone = clone $this; + + // 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(); + + $countResults = $results->count(); + + if ($countResults == 0) { + break; + } + + // On each chunk result set, we will pass them to the callback and then let the + // developer take care of everything within the callback, which allows us to + // keep the memory low for spinning through large result sets for working. + if ($callback($results) === false) { + return false; + } + + $lastId = $results->last()->{$alias}; + + unset($results); + } while ($countResults == $count); + + return true; + } + + /** + * Execute a callback over each item while chunking by id. + * + * @param callable $callback + * @param int $count + * @param string $column + * @param string $alias + * @return bool + */ + public function eachById(callable $callback, $count = 1000, $column = null, $alias = null) + { + return $this->chunkById($count, function ($results) use ($callback) { + foreach ($results as $key => $value) { + if ($callback($value, $key) === false) { + return false; + } + } + }, $column, $alias); + } + /** * Execute the query and get the first result. * diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index d968e6a6df0b..9741309cab40 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -646,49 +646,13 @@ public function cursor() } /** - * Chunk the results of a query by comparing numeric IDs. + * Get the default key name of the table. * - * @param int $count - * @param callable $callback - * @param string|null $column - * @param string|null $alias - * @return bool + * @return string */ - public function chunkById($count, callable $callback, $column = null, $alias = null) + protected function defaultKeyName() { - $column = is_null($column) ? $this->getModel()->getKeyName() : $column; - - $alias = is_null($alias) ? $column : $alias; - - $lastId = null; - - do { - $clone = clone $this; - - // 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(); - - $countResults = $results->count(); - - if ($countResults == 0) { - break; - } - - // On each chunk result set, we will pass them to the callback and then let the - // developer take care of everything within the callback, which allows us to - // keep the memory low for spinning through large result sets for working. - if ($callback($results) === false) { - return false; - } - - $lastId = $results->last()->{$alias}; - - unset($results); - } while ($countResults == $count); - - return true; + return $this->getModel()->getKeyName(); } /** diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 56e0a253825d..f6aa042251e3 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -2173,47 +2173,13 @@ public function cursor() } /** - * Chunk the results of a query by comparing numeric IDs. + * Get the default key name of the table. * - * @param int $count - * @param callable $callback - * @param string $column - * @param string|null $alias - * @return bool + * @return string */ - public function chunkById($count, callable $callback, $column = 'id', $alias = null) + protected function defaultKeyName() { - $alias = $alias ?: $column; - - $lastId = null; - - do { - $clone = clone $this; - - // 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(); - - $countResults = $results->count(); - - if ($countResults == 0) { - break; - } - - // On each chunk result set, we will pass them to the callback and then let the - // developer take care of everything within the callback, which allows us to - // keep the memory low for spinning through large result sets for working. - if ($callback($results) === false) { - return false; - } - - $lastId = $results->last()->{$alias}; - - unset($results); - } while ($countResults == $count); - - return true; + return 'id'; } /** diff --git a/tests/Database/DatabaseEloquentIntegrationTest.php b/tests/Database/DatabaseEloquentIntegrationTest.php index ba92c0444e0c..a86efd939f51 100644 --- a/tests/Database/DatabaseEloquentIntegrationTest.php +++ b/tests/Database/DatabaseEloquentIntegrationTest.php @@ -401,6 +401,20 @@ public function testChunkByIdWithNonIncrementingKey() $this->assertEquals(2, $i); } + public function testEachByIdWithNonIncrementingKey() + { + EloquentTestNonIncrementingSecond::create(['name' => ' First']); + EloquentTestNonIncrementingSecond::create(['name' => ' Second']); + EloquentTestNonIncrementingSecond::create(['name' => ' Third']); + + $users = []; + EloquentTestNonIncrementingSecond::query()->eachById( + function (EloquentTestNonIncrementingSecond $user, $i) use (&$users) { + $users[] = [$user->name, $i]; + }, 2, 'name'); + $this->assertSame([[' First', 0], [' Second', 1], [' Third', 0]], $users); + } + public function testPluck() { EloquentTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']);