From e9edd6f0a289c6bf87a34f6f62d7b487faf1325b Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 14 Apr 2020 22:42:17 +0700 Subject: [PATCH 1/5] Add support belongsToMany --- src/EloquentJoinBuilder.php | 42 +++++++++++++++---- src/Exceptions/InvalidRelationClause.php | 2 +- src/Relations/BelongsToManyJoin.php | 11 +++++ src/Traits/ExtendRelationsTrait.php | 7 ++++ tests/Models/City.php | 2 +- tests/Models/Seller.php | 5 +++ tests/Models/Tag.php | 24 +++++++++++ tests/Models/User.php | 5 +++ tests/TestCase.php | 2 +- tests/Tests/ExceptionTest.php | 3 +- tests/Tests/Relations/BelongToMany.php | 22 ++++++++++ tests/Tests/Relations/HasManyTest.php | 15 +++++++ .../2017_11_04_163552_create_database.php | 13 ++++++ 13 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/Relations/BelongsToManyJoin.php create mode 100644 tests/Models/Tag.php create mode 100644 tests/Tests/Relations/BelongToMany.php diff --git a/src/EloquentJoinBuilder.php b/src/EloquentJoinBuilder.php index ee71ece..d795b7c 100644 --- a/src/EloquentJoinBuilder.php +++ b/src/EloquentJoinBuilder.php @@ -9,6 +9,7 @@ use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationGlobalScope; use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationWhere; use Fico7489\Laravel\EloquentJoin\Relations\BelongsToJoin; +use Fico7489\Laravel\EloquentJoin\Relations\BelongsToManyJoin; use Fico7489\Laravel\EloquentJoin\Relations\HasManyJoin; use Fico7489\Laravel\EloquentJoin\Relations\HasOneJoin; use Illuminate\Database\Eloquent\Builder; @@ -170,27 +171,32 @@ protected function performJoin($relations, $leftJoin = null) $currentTableAlias = $baseTable; $relationsAccumulated = []; - foreach ($relations as $relation) { - if ($relation == $column) { - //last item in $relations argument is sort|where column - break; - } + for ($i = 0; $i < count($relations) - 1; ++$i) { + $relation = $relations[$i]; + $nextRelation = $relations[$i + 1]; /** @var Relation $relatedRelation */ $relatedRelation = $currentModel->$relation(); $relatedModel = $relatedRelation->getRelated(); $relatedPrimaryKey = $relatedModel->getKeyName(); $relatedTable = $relatedModel->getTable(); - $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable; - $relationsAccumulated[] = $relatedTableAlias; + $relationsAccumulated[] = $relatedTable; $relationAccumulatedString = implode('_', $relationsAccumulated); + $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable; + //relations count if ($this->appendRelationsCount) { $this->selectRaw('COUNT('.$relatedTableAlias.'.'.$relatedPrimaryKey.') as '.$relationAccumulatedString.'_count'); } + if ($relatedRelation instanceof BelongsToManyJoin) { + $pivotTable = $relatedRelation->getTable(); + $pivotTableAlias = $this->useTableAlias ? sha1($pivotTable) : $pivotTable; + $joinQueryPivot = $pivotTable.($this->useTableAlias ? ' as '.$pivotTableAlias : ''); + } + if (!in_array($relationAccumulatedString, $this->joinedTables)) { $joinQuery = $relatedTable.($this->useTableAlias ? ' as '.$relatedTableAlias : ''); if ($relatedRelation instanceof BelongsToJoin) { @@ -212,6 +218,23 @@ protected function performJoin($relations, $leftJoin = null) $this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $currentTableAlias, $localKey) { $join->on($relatedTableAlias.'.'.$relatedKey, '=', $currentTableAlias.'.'.$localKey); + $this->joinQuery($join, $relatedRelation, $relatedTableAlias); + }); + } elseif ($relatedRelation instanceof BelongsToManyJoin) { + $localPivotKey = $relatedRelation->getForeignPivotKeyName(); + $relatedPivotKey = $relatedRelation->getRelatedPivotKeyName(); + $localKey = $relatedRelation->getParentKeyName(); + $relatedKey = $relatedRelation->getRelatedKeyName(); + + $this->$joinMethod($joinQueryPivot, function ($join) use ($relatedRelation, $pivotTableAlias, $localPivotKey, $currentTableAlias, $localKey) { + $join->on($pivotTableAlias.'.'.$localPivotKey, '=', $currentTableAlias.'.'.$localKey); + + $this->joinQuery($join, $relatedRelation, $pivotTableAlias); + }); + + $this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $pivotTableAlias, $relatedPivotKey) { + $join->on($relatedTableAlias.'.'.$relatedKey, '=', $pivotTableAlias.'.'.$relatedPivotKey); + $this->joinQuery($join, $relatedRelation, $relatedTableAlias); }); } else { @@ -219,6 +242,11 @@ protected function performJoin($relations, $leftJoin = null) } } + if ('pivot' === $nextRelation) { + $relatedTableAlias = $pivotTableAlias; + ++$i; + } + $currentModel = $relatedModel; $currentTableAlias = $relatedTableAlias; diff --git a/src/Exceptions/InvalidRelationClause.php b/src/Exceptions/InvalidRelationClause.php index f2b205e..05135aa 100644 --- a/src/Exceptions/InvalidRelationClause.php +++ b/src/Exceptions/InvalidRelationClause.php @@ -4,5 +4,5 @@ class InvalidRelationClause extends \Exception { - public $message = 'Package allows only following clauses on relation : where, orWhere, withTrashed, onlyTrashed and withoutTrashed.'; + public $message = 'Package allows only following clauses on relation : where, orWhere, whereRaw, orderByRaw, withTrashed, onlyTrashed and withoutTrashed.'; } diff --git a/src/Relations/BelongsToManyJoin.php b/src/Relations/BelongsToManyJoin.php new file mode 100644 index 0000000..4dd6a39 --- /dev/null +++ b/src/Relations/BelongsToManyJoin.php @@ -0,0 +1,11 @@ +belongsToMany(Seller::class, 'locations', 'seller_id', 'city_id'); + return $this->belongsToMany(Seller::class, 'locations', 'city_id', 'seller_id'); } public function zipCodes() diff --git a/tests/Models/Seller.php b/tests/Models/Seller.php index b49b5ee..8770706 100644 --- a/tests/Models/Seller.php +++ b/tests/Models/Seller.php @@ -63,4 +63,9 @@ public function city() { return $this->belongsTo(City::class); } + + public function tags() + { + return $this->morphToMany(Tag::class, 'taggable'); + } } diff --git a/tests/Models/Tag.php b/tests/Models/Tag.php new file mode 100644 index 0000000..63ab4fa --- /dev/null +++ b/tests/Models/Tag.php @@ -0,0 +1,24 @@ +morphedByMany(Seller::class, 'taggable'); + } + + public function users() + { + return $this->morphedByMany(User::class, 'taggable'); + } +} diff --git a/tests/Models/User.php b/tests/Models/User.php index 1bcdaf1..ab1f508 100644 --- a/tests/Models/User.php +++ b/tests/Models/User.php @@ -11,4 +11,9 @@ class User extends BaseModel protected $table = 'users'; protected $fillable = ['name']; + + public function tags() + { + return $this->morphToMany(Tag::class, 'taggable'); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 0714b5e..12b4593 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -120,6 +120,6 @@ protected function assertQueryMatches($expected, $actual) $expected = str_replace(['"'], '`', $expected); } - $this->assertRegExp($expected, $actual); + $this->assertMatchesRegularExpression($expected, $actual); } } diff --git a/tests/Tests/ExceptionTest.php b/tests/Tests/ExceptionTest.php index 06e5602..e48dfa5 100644 --- a/tests/Tests/ExceptionTest.php +++ b/tests/Tests/ExceptionTest.php @@ -8,7 +8,6 @@ use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationClause; use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationGlobalScope; use Fico7489\Laravel\EloquentJoin\Exceptions\InvalidRelationWhere; -use Fico7489\Laravel\EloquentJoin\Tests\Models\City; use Fico7489\Laravel\EloquentJoin\Tests\Models\Seller; use Fico7489\Laravel\EloquentJoin\Tests\TestCase; @@ -17,7 +16,7 @@ class ExceptionTest extends TestCase public function testInvalidRelation() { try { - City::whereJoin('sellers.id', '=', 'test')->get(); + Seller::whereJoin('tags.name', '=', 'test')->get(); } catch (InvalidRelation $e) { $this->assertEquals((new InvalidRelation())->message, $e->getMessage()); diff --git a/tests/Tests/Relations/BelongToMany.php b/tests/Tests/Relations/BelongToMany.php new file mode 100644 index 0000000..d2e76e6 --- /dev/null +++ b/tests/Tests/Relations/BelongToMany.php @@ -0,0 +1,22 @@ +get(); + + $queryTest = 'select cities.* from "cities" + left join "locations" on "locations"."city_id" = "cities"."id" + left join "sellers" on "sellers"."id" = "locations"."seller_id" + where "cities"."deleted_at" is null + group by "cities"."id"'; + + $this->assertQueryMatches($queryTest, $this->fetchQuery()); + } +} diff --git a/tests/Tests/Relations/HasManyTest.php b/tests/Tests/Relations/HasManyTest.php index 3912178..321aaad 100644 --- a/tests/Tests/Relations/HasManyTest.php +++ b/tests/Tests/Relations/HasManyTest.php @@ -3,6 +3,7 @@ namespace Fico7489\Laravel\EloquentJoin\Tests\Tests\Relations; use Fico7489\Laravel\EloquentJoin\Tests\Models\Seller; +use Fico7489\Laravel\EloquentJoin\Tests\Models\State; use Fico7489\Laravel\EloquentJoin\Tests\TestCase; class HasManyTest extends TestCase @@ -48,4 +49,18 @@ public function testHasManyBelongsTo() $this->assertQueryMatches($queryTest, $this->fetchQuery()); } + + public function testHasManyBelongsToMany() + { + State::joinRelations('cities.sellers')->get(); + + $queryTest = 'select states.* from "states" + left join "cities" on "cities"."state_id" = "states"."id" and "cities"."deleted_at" is null + left join "locations" on "locations"."city_id" = "cities"."id" + left join "sellers" on "sellers"."id" = "locations"."seller_id" + where "states"."deleted_at" is null + group by "states"."id"'; + + $this->assertQueryMatches($queryTest, $this->fetchQuery()); + } } diff --git a/tests/database/migrations/2017_11_04_163552_create_database.php b/tests/database/migrations/2017_11_04_163552_create_database.php index 4a0f812..50725fe 100644 --- a/tests/database/migrations/2017_11_04_163552_create_database.php +++ b/tests/database/migrations/2017_11_04_163552_create_database.php @@ -153,6 +153,19 @@ public function up() $table->unsignedInteger('id_seller_foreign')->nullable(); $table->foreign('id_seller_foreign')->references('id')->on('sellers'); }); + + Schema::create('tags', function (Blueprint $table) { + $table->increments('id_location_primary'); + + $table->string('name'); + }); + + Schema::create('taggables', function (Blueprint $table) { + $table->increments('tag_id'); + + $table->unsignedInteger('taggable_id'); + $table->string('taggable_type'); + }); } /** From 52ecea87b3772cf60775afc107ea274a6fe2763e Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 14 Apr 2020 22:46:13 +0700 Subject: [PATCH 2/5] Replace assertMatchesRegularExpression to assertRegExp --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 12b4593..0714b5e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -120,6 +120,6 @@ protected function assertQueryMatches($expected, $actual) $expected = str_replace(['"'], '`', $expected); } - $this->assertMatchesRegularExpression($expected, $actual); + $this->assertRegExp($expected, $actual); } } From c19dc0ed5b0afe1aca6c9d6810a48a1e6d565116 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 14 Apr 2020 23:34:09 +0700 Subject: [PATCH 3/5] Refactor --- src/EloquentJoinBuilder.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EloquentJoinBuilder.php b/src/EloquentJoinBuilder.php index d795b7c..41110f3 100644 --- a/src/EloquentJoinBuilder.php +++ b/src/EloquentJoinBuilder.php @@ -180,12 +180,11 @@ protected function performJoin($relations, $leftJoin = null) $relatedModel = $relatedRelation->getRelated(); $relatedPrimaryKey = $relatedModel->getKeyName(); $relatedTable = $relatedModel->getTable(); + $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable; $relationsAccumulated[] = $relatedTable; $relationAccumulatedString = implode('_', $relationsAccumulated); - $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable; - //relations count if ($this->appendRelationsCount) { $this->selectRaw('COUNT('.$relatedTableAlias.'.'.$relatedPrimaryKey.') as '.$relationAccumulatedString.'_count'); From 770f4e41e00a56367b7544ad892f69d98f2c6885 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 14 Apr 2020 23:34:59 +0700 Subject: [PATCH 4/5] Refactor --- src/EloquentJoinBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EloquentJoinBuilder.php b/src/EloquentJoinBuilder.php index 41110f3..7d6bc1e 100644 --- a/src/EloquentJoinBuilder.php +++ b/src/EloquentJoinBuilder.php @@ -182,7 +182,7 @@ protected function performJoin($relations, $leftJoin = null) $relatedTable = $relatedModel->getTable(); $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable) : $relatedTable; - $relationsAccumulated[] = $relatedTable; + $relationsAccumulated[] = $relatedTableAlias; $relationAccumulatedString = implode('_', $relationsAccumulated); //relations count From 499e07077f7208f59c07755adedc9baaf3586bc4 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 14 Apr 2020 23:39:53 +0700 Subject: [PATCH 5/5] Fix exception messages --- src/Exceptions/InvalidRelation.php | 2 +- src/Exceptions/InvalidRelationClause.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Exceptions/InvalidRelation.php b/src/Exceptions/InvalidRelation.php index e1caac6..3dabb57 100644 --- a/src/Exceptions/InvalidRelation.php +++ b/src/Exceptions/InvalidRelation.php @@ -4,5 +4,5 @@ class InvalidRelation extends \Exception { - public $message = 'Package allows only following relations : BelongsTo, HasOne and HasMany.'; + public $message = 'Package allows only following relations : BelongsTo, BelongsToMany, HasOne and HasMany.'; } diff --git a/src/Exceptions/InvalidRelationClause.php b/src/Exceptions/InvalidRelationClause.php index 05135aa..f2b205e 100644 --- a/src/Exceptions/InvalidRelationClause.php +++ b/src/Exceptions/InvalidRelationClause.php @@ -4,5 +4,5 @@ class InvalidRelationClause extends \Exception { - public $message = 'Package allows only following clauses on relation : where, orWhere, whereRaw, orderByRaw, withTrashed, onlyTrashed and withoutTrashed.'; + public $message = 'Package allows only following clauses on relation : where, orWhere, withTrashed, onlyTrashed and withoutTrashed.'; }