diff --git a/src/HasManyMerged.php b/src/HasManyMerged.php index 2a27676..fb582bc 100644 --- a/src/HasManyMerged.php +++ b/src/HasManyMerged.php @@ -73,6 +73,16 @@ public function getParentKey() return $this->parent->getAttribute($this->localKey); } + /** + * Get the fully qualified parent key name. + * + * @return string + */ + public function getQualifiedParentKeyName() + { + return $this->parent->qualifyColumn($this->localKey); + } + /** * Set the constraints for an eager load of the relation. * Note: Used to load relations of multiple models at once. @@ -91,6 +101,30 @@ public function addEagerConstraints(array $models) }); } + /** + * Add the constraints for an internal relationship existence query. + * + * Essentially, these queries compare on column names like whereColumn. + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Illuminate\Database\Eloquent\Builder $parentQuery + * @param array|mixed $columns + * @return \Illuminate\Database\Eloquent\Builder + */ + public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*']) + { + $foreignKeys = $this->foreignKeys; + + return $query->select($columns)->where(function ($query) use ($foreignKeys) { + foreach ($foreignKeys as $foreignKey) { + $query->orWhere(function ($query) use ($foreignKey) { + $query->whereColumn($this->getQualifiedParentKeyName(), '=', $foreignKey) + ->whereNotNull($foreignKey); + }); + } + }); + } + /** * Get the name of the "where in" method for eager loading. * Note: Similar to whereInMethod of Relation class. diff --git a/tests/HasManyMergedTest.php b/tests/HasManyMergedTest.php index 8a2cb23..7cdb5ae 100644 --- a/tests/HasManyMergedTest.php +++ b/tests/HasManyMergedTest.php @@ -2,13 +2,14 @@ namespace Korridor\LaravelHasManyMerged\Tests; +use Illuminate\Database\Eloquent\Builder; use Korridor\LaravelHasManyMerged\HasManyMerged; use Korridor\LaravelHasManyMerged\Tests\Models\Message; use Korridor\LaravelHasManyMerged\Tests\Models\User; class HasManyMergedTest extends TestCase { - private function createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages() + private function createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(): void { User::create([ 'id' => 11, @@ -26,28 +27,32 @@ private function createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages( 'content' => 'A - This is a message!', 'sender_user_id' => 1, 'receiver_user_id' => 1, + 'content_integer' => 1, ]); Message::create([ 'id' => 2, 'content' => 'B - This is a message!', 'sender_user_id' => 1, 'receiver_user_id' => 2, + 'content_integer' => 1, ]); Message::create([ 'id' => 3, 'content' => 'C - This is a message!', 'sender_user_id' => 2, 'receiver_user_id' => 1, + 'content_integer' => 1, ]); Message::create([ 'id' => 4, 'content' => 'D - This is a message!', 'sender_user_id' => 2, 'receiver_user_id' => 2, + 'content_integer' => 2, ]); } - public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithLazyLoading() + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithLazyLoading(): void { // Arrange $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); @@ -71,7 +76,7 @@ public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSam $this->assertEquals(2, $user2->sentMessages()->count()); } - public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoading() + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoading(): void { // Arrange $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); @@ -96,7 +101,83 @@ public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSam $this->assertEquals(2, $user2->sentMessages()->count()); } - public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithLazyEagerLoading() + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoadingWithCount(): void + { + // Arrange + $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); + + // Act + $this->db::connection()->enableQueryLog(); + $users = User::with(['messages'])->withCount(['messages', 'sentMessages'])->get(); + $user1 = $users->firstWhere('id', 11); + $user2 = $users->firstWhere('id', 12); + $messagesOfUser1 = $user1->messages; + $messagesOfUser2 = $user2->messages; + $queries = $this->db::getQueryLog(); + $this->db::connection()->disableQueryLog(); + + // Assert + $this->assertEquals(2, count($queries)); + $this->assertEquals(3, $messagesOfUser1->count()); + $this->assertEquals(3, $messagesOfUser2->count()); + $this->assertEquals(3, $user1->messages_count); + $this->assertEquals(3, $user2->messages_count); + $this->assertEquals(2, $user1->receivedMessages()->count()); + $this->assertEquals(2, $user1->sentMessages()->count()); + $this->assertEquals(2, $user2->receivedMessages()->count()); + $this->assertEquals(2, $user2->sentMessages()->count()); + } + + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoadingWithSum(): void + { + if (! method_exists(Builder::class, 'withSum')) { + $this->markTestSkipped('withSum is not supported'); + } + + // Arrange + $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); + + // Act + $this->db::connection()->enableQueryLog(); + $users = User::with(['messages'])->withSum('messages', 'content_integer')->get(); + $user1 = $users->firstWhere('id', 11); + $user2 = $users->firstWhere('id', 12); + $messagesOfUser1 = $user1->messages; + $messagesOfUser2 = $user2->messages; + $queries = $this->db::getQueryLog(); + $this->db::connection()->disableQueryLog(); + + // Assert + $this->assertEquals(2, count($queries)); + $this->assertEquals(3, $messagesOfUser1->count()); + $this->assertEquals(3, $messagesOfUser2->count()); + $this->assertEquals(3, $user1->messages_sum_content_integer); + $this->assertEquals(4, $user2->messages_sum_content_integer); + $this->assertEquals(2, $user1->receivedMessages()->count()); + $this->assertEquals(2, $user1->sentMessages()->count()); + $this->assertEquals(2, $user2->receivedMessages()->count()); + $this->assertEquals(2, $user2->sentMessages()->count()); + } + + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoadingWhereHas(): void + { + // Arrange + $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); + + // Act + $this->db::connection()->enableQueryLog(); + $users = User::with(['messages'])->whereHas('messages', function (Builder $builder) { + $builder->where('content_integer', '=', 2); + })->get(); + $queries = $this->db::getQueryLog(); + $this->db::connection()->disableQueryLog(); + + // Assert + $this->assertEquals(1, count($users)); + $this->assertEquals(2, $users->first()->other_unique_id); + } + + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithLazyEagerLoading(): void { // Arrange $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); @@ -122,7 +203,7 @@ public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSam $this->assertEquals(2, $user2->sentMessages()->count()); } - public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithConstrainedEagerLoading() + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithConstrainedEagerLoading(): void { // Arrange $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); @@ -151,7 +232,7 @@ public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSam $this->assertEquals(2, $user2->sentMessages()->count()); } - public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoadingAndOrderCheck() + public function testHasManyMergedWithTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessagesWithEagerLoadingAndOrderCheck(): void { // Arrange $this->createTwoUsersWereBothAreSenderOrReceiverOfTheSameFourMessages(); diff --git a/tests/Models/Message.php b/tests/Models/Message.php index 6ddbddb..3327d75 100644 --- a/tests/Models/Message.php +++ b/tests/Models/Message.php @@ -18,6 +18,7 @@ class Message extends Model protected $fillable = [ 'id', 'content', + 'content_integer', 'sender_user_id', 'receiver_user_id', ]; diff --git a/tests/TestCase.php b/tests/TestCase.php index b7077d9..104302a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -34,6 +34,7 @@ protected function setUp(): void $this->db::schema()->create('messages', function (Blueprint $table) { $table->increments('id'); $table->text('content'); + $table->integer('content_integer')->default(0); $table->unsignedInteger('sender_user_id'); $table->unsignedInteger('receiver_user_id'); $table->timestamps();