From eac4575d3720b71c2ee145c7cfc20a5fa10a11ba Mon Sep 17 00:00:00 2001 From: Lonny Kapelushnik Date: Tue, 9 Apr 2024 20:19:57 -0600 Subject: [PATCH 1/3] Added failing test --- .../Database/DatabaseEloquentIntegrationTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Database/DatabaseEloquentIntegrationTest.php b/tests/Database/DatabaseEloquentIntegrationTest.php index 3dbb553891c9..a3e3c1fa312e 100644 --- a/tests/Database/DatabaseEloquentIntegrationTest.php +++ b/tests/Database/DatabaseEloquentIntegrationTest.php @@ -100,6 +100,7 @@ protected function createSchema() }); $this->schema($connection)->create('friends', function ($table) { + $table->increments('id'); $table->integer('user_id'); $table->integer('friend_id'); $table->integer('friend_level_id')->nullable(); @@ -702,6 +703,21 @@ function (EloquentTestNonIncrementingSecond $user, $i) use (&$users) { $this->assertSame([[' First', 0], [' Second', 1], [' Third', 2]], $users); } + public function testEachByIdOnPivotWithIncrementId() + { + $user = EloquentTestUser::create(['id' => 2, 'email' => 'taylorotwel@gmail.com']); + $user2 = EloquentTestUser::create(['id' => 3, 'email' => 'abigailotwel@gmail.com']);; + $user->friends()->attach($user2); + + $user->friends()->eachById( + function (EloquentTestUser $model) use (&$models) { + $models[] = $model; + } + ); + + $this->assertSame($user2->id, $models[0]->id); + } + public function testPluck() { EloquentTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']); From 936f87d854d91ff91c3c3090d0b914ccd25e493a Mon Sep 17 00:00:00 2001 From: Lonny Kapelushnik Date: Tue, 9 Apr 2024 21:23:57 -0600 Subject: [PATCH 2/3] Added eachById and chunkByIdDesc to BelongsToMany --- .../Eloquent/Relations/BelongsToMany.php | 55 ++++++- ...baseEloquentBelongsToManyChunkByIdTest.php | 31 ++-- ...abaseEloquentBelongsToManyEachByIdTest.php | 135 ++++++++++++++++++ .../DatabaseEloquentIntegrationTest.php | 16 --- 4 files changed, 209 insertions(+), 28 deletions(-) create mode 100644 tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index c0459b4d7267..73152d7e5879 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -997,19 +997,66 @@ public function chunk($count, callable $callback) */ public function chunkById($count, callable $callback, $column = null, $alias = null) { - $this->prepareQueryBuilder(); + 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); + } + /** + * Execute a callback over each item while chunking by ID. + * + * @param callable $callback + * @param int $count + * @param string|null $column + * @param string|null $alias + * @return bool + */ + public function eachById(callable $callback, $count = 1000, $column = null, $alias = null) + { + return $this->chunkById($count, function ($results, $page) use ($callback, $count) { + foreach ($results as $key => $value) { + if ($callback($value, (($page - 1) * $count) + $key) === false) { + return false; + } + } + }, $column, $alias); + } + + /** + * 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->getRelated()->qualifyColumn( $this->getRelatedKeyName() ); $alias ??= $this->getRelatedKeyName(); - return $this->query->chunkById($count, function ($results) use ($callback) { + return $this->prepareQueryBuilder()->orderedChunkById($count, function ($results, $page) use ($callback) { $this->hydratePivotRelation($results->all()); - return $callback($results); - }, $column, $alias); + return $callback($results, $page); + }, $column, $alias, $descending); } /** diff --git a/tests/Database/DatabaseEloquentBelongsToManyChunkByIdTest.php b/tests/Database/DatabaseEloquentBelongsToManyChunkByIdTest.php index f35c2f9a3ce9..0b9d8d51d93c 100644 --- a/tests/Database/DatabaseEloquentBelongsToManyChunkByIdTest.php +++ b/tests/Database/DatabaseEloquentBelongsToManyChunkByIdTest.php @@ -37,13 +37,14 @@ public function createSchema() }); $this->schema()->create('articles', function ($table) { - $table->increments('aid'); + $table->increments('id'); $table->string('title'); }); $this->schema()->create('article_user', function ($table) { + $table->increments('id'); $table->integer('article_id')->unsigned(); - $table->foreign('article_id')->references('aid')->on('articles'); + $table->foreign('article_id')->references('id')->on('articles'); $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users'); }); @@ -58,7 +59,22 @@ public function testBelongsToChunkById() $user->articles()->chunkById(1, function (Collection $collection) use (&$i) { $i++; - $this->assertEquals($i, $collection->first()->aid); + $this->assertEquals($i, $collection->first()->id); + }); + + $this->assertSame(3, $i); + } + + public function testBelongsToChunkByIdDesc() + { + $this->seedData(); + + $user = BelongsToManyChunkByIdTestTestUser::query()->first(); + $i = 0; + + $user->articles()->chunkByIdDesc(1, function (Collection $collection) use (&$i) { + $this->assertEquals(3 - $i, $collection->first()->id); + $i++; }); $this->assertSame(3, $i); @@ -83,9 +99,9 @@ protected function seedData() { $user = BelongsToManyChunkByIdTestTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']); BelongsToManyChunkByIdTestTestArticle::query()->insert([ - ['aid' => 1, 'title' => 'Another title'], - ['aid' => 2, 'title' => 'Another title'], - ['aid' => 3, 'title' => 'Another title'], + ['id' => 1, 'title' => 'Another title'], + ['id' => 2, 'title' => 'Another title'], + ['id' => 3, 'title' => 'Another title'], ]); $user->articles()->sync([3, 1, 2]); @@ -126,10 +142,9 @@ public function articles() class BelongsToManyChunkByIdTestTestArticle extends Eloquent { - protected $primaryKey = 'aid'; protected $table = 'articles'; protected $keyType = 'string'; public $incrementing = false; public $timestamps = false; - protected $fillable = ['aid', 'title']; + protected $fillable = ['id', 'title']; } diff --git a/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php b/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php new file mode 100644 index 000000000000..e101fe659eaa --- /dev/null +++ b/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php @@ -0,0 +1,135 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + + $db->bootEloquent(); + $db->setAsGlobal(); + + $this->createSchema(); + } + + /** + * Setup the database schema. + * + * @return void + */ + public function createSchema() + { + $this->schema()->create('users', function ($table) { + $table->increments('id'); + $table->string('email')->unique(); + }); + + $this->schema()->create('articles', function ($table) { + $table->increments('id'); + $table->string('title'); + }); + + $this->schema()->create('article_user', function ($table) { + $table->increments('id'); + $table->integer('article_id')->unsigned(); + $table->foreign('article_id')->references('id')->on('articles'); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users'); + }); + } + + public function testBelongsToEachById() + { + $this->seedData(); + + $user = BelongsToManyEachByIdTestTestUser::query()->first(); + $i = 0; + + $user->articles()->eachById(function (BelongsToManyEachByIdTestTestArticle $model) use (&$i) { + $i++; + $this->assertEquals($i, $model->id); + }); + + $this->assertSame(3, $i); + } + + /** + * Tear down the database schema. + * + * @return void + */ + protected function tearDown(): void + { + $this->schema()->drop('users'); + $this->schema()->drop('articles'); + $this->schema()->drop('article_user'); + } + + /** + * Helpers... + */ + protected function seedData() + { + $user = BelongsToManyEachByIdTestTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']); + BelongsToManyEachByIdTestTestArticle::query()->insert([ + ['id' => 1, 'title' => 'Another title'], + ['id' => 2, 'title' => 'Another title'], + ['id' => 3, 'title' => 'Another title'], + ]); + + $user->articles()->sync([3, 1, 2]); + } + + /** + * Get a database connection instance. + * + * @return \Illuminate\Database\ConnectionInterface + */ + protected function connection() + { + return Eloquent::getConnectionResolver()->connection(); + } + + /** + * Get a schema builder instance. + * + * @return \Illuminate\Database\Schema\Builder + */ + protected function schema() + { + return $this->connection()->getSchemaBuilder(); + } +} + +class BelongsToManyEachByIdTestTestUser extends Eloquent +{ + protected $table = 'users'; + protected $fillable = ['id', 'email']; + public $timestamps = false; + + public function articles() + { + return $this->belongsToMany(BelongsToManyEachByIdTestTestArticle::class, 'article_user', 'user_id', 'article_id'); + } +} + +class BelongsToManyEachByIdTestTestArticle extends Eloquent +{ + protected $table = 'articles'; + protected $keyType = 'string'; + public $incrementing = false; + public $timestamps = false; + protected $fillable = ['id', 'title']; +} diff --git a/tests/Database/DatabaseEloquentIntegrationTest.php b/tests/Database/DatabaseEloquentIntegrationTest.php index a3e3c1fa312e..3dbb553891c9 100644 --- a/tests/Database/DatabaseEloquentIntegrationTest.php +++ b/tests/Database/DatabaseEloquentIntegrationTest.php @@ -100,7 +100,6 @@ protected function createSchema() }); $this->schema($connection)->create('friends', function ($table) { - $table->increments('id'); $table->integer('user_id'); $table->integer('friend_id'); $table->integer('friend_level_id')->nullable(); @@ -703,21 +702,6 @@ function (EloquentTestNonIncrementingSecond $user, $i) use (&$users) { $this->assertSame([[' First', 0], [' Second', 1], [' Third', 2]], $users); } - public function testEachByIdOnPivotWithIncrementId() - { - $user = EloquentTestUser::create(['id' => 2, 'email' => 'taylorotwel@gmail.com']); - $user2 = EloquentTestUser::create(['id' => 3, 'email' => 'abigailotwel@gmail.com']);; - $user->friends()->attach($user2); - - $user->friends()->eachById( - function (EloquentTestUser $model) use (&$models) { - $models[] = $model; - } - ); - - $this->assertSame($user2->id, $models[0]->id); - } - public function testPluck() { EloquentTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']); From ad7f2ae8af301f274672f7c85dcf9323468d482c Mon Sep 17 00:00:00 2001 From: Lonny Kapelushnik Date: Tue, 9 Apr 2024 21:37:51 -0600 Subject: [PATCH 3/3] Removed extra namespace --- tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php b/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php index e101fe659eaa..0a2fe1e97a06 100644 --- a/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php +++ b/tests/Database/DatabaseEloquentBelongsToManyEachByIdTest.php @@ -3,7 +3,6 @@ namespace Illuminate\Tests\Database; use Illuminate\Database\Capsule\Manager as DB; -use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model as Eloquent; use PHPUnit\Framework\TestCase;