From 110b282d280704fe0c62fe7b1482c3c414dec41f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 16 Mar 2017 11:14:55 -0500 Subject: [PATCH] fix various problems with belongs to many --- .../Eloquent/Concerns/HasRelationships.php | 51 +++++++++++-------- .../Eloquent/Relations/BelongsToMany.php | 36 ++++++------- .../Concerns/InteractsWithPivotTable.php | 16 +++--- .../Eloquent/Relations/MorphToMany.php | 17 ++++--- tests/Database/DatabaseEloquentModelTest.php | 8 +-- 5 files changed, 71 insertions(+), 57 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php index 13637fa2840d..6f054bd4a554 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php @@ -284,15 +284,15 @@ public function morphMany($related, $name, $type = null, $id = null, $localKey = * * @param string $related * @param string $table - * @param string $foreignKey - * @param string $relatedKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey * @param string $parentKey - * @param string $localKey + * @param string $relatedKey * @param string $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ - public function belongsToMany($related, $table = null, $foreignKey = null, $relatedKey = null, - $parentKey = null, $localKey = null, $relation = null) + public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null, + $parentKey = null, $relatedKey = null, $relation = null) { // If no relationship name was passed, we will pull backtraces to get the // name of the calling function. We will use that function name as the @@ -306,9 +306,9 @@ public function belongsToMany($related, $table = null, $foreignKey = null, $rela // instances as well as the relationship instances we need for this. $instance = $this->newRelatedInstance($related); - $foreignKey = $foreignKey ?: $this->getForeignKey(); + $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey(); - $relatedKey = $relatedKey ?: $instance->getForeignKey(); + $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey(); // If no table name was provided, we can guess it by concatenating the two // models using underscores in alphabetical order. The two model names @@ -318,9 +318,9 @@ public function belongsToMany($related, $table = null, $foreignKey = null, $rela } return new BelongsToMany( - $instance->newQuery(), $this, $table, $foreignKey, - $relatedKey, $parentKey ?: $this->getKeyName(), - $localKey ?: $instance->getKeyName(), $relation + $instance->newQuery(), $this, $table, $foreignPivotKey, + $relatedPivotKey, $parentKey ?: $this->getKeyName(), + $relatedKey ?: $instance->getKeyName(), $relation ); } @@ -330,12 +330,16 @@ public function belongsToMany($related, $table = null, $foreignKey = null, $rela * @param string $related * @param string $name * @param string $table - * @param string $foreignKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey + * @param string $parentKey * @param string $relatedKey * @param bool $inverse * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ - public function morphToMany($related, $name, $table = null, $foreignKey = null, $relatedKey = null, $inverse = false) + public function morphToMany($related, $name, $table = null, $foreignPivotKey = null, + $relatedPivotKey = null, $parentKey = null, + $relatedKey = null, $inverse = false) { $caller = $this->guessBelongsToManyRelation(); @@ -344,9 +348,9 @@ public function morphToMany($related, $name, $table = null, $foreignKey = null, // instances, as well as the relationship instances we need for these. $instance = $this->newRelatedInstance($related); - $foreignKey = $foreignKey ?: $name.'_id'; + $foreignPivotKey = $foreignPivotKey ?: $name.'_id'; - $relatedKey = $relatedKey ?: $instance->getForeignKey(); + $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey(); // Now we're ready to create a new query builder for this related model and // the relationship instances for this relation. This relations will set @@ -355,7 +359,8 @@ public function morphToMany($related, $name, $table = null, $foreignKey = null, return new MorphToMany( $instance->newQuery(), $this, $name, $table, - $foreignKey, $relatedKey, $caller, $inverse + $foreignPivotKey, $relatedPivotKey, $parentKey ?: $this->getKeyName(), + $relatedKey ?: $instance->getKeyName(), $caller, $inverse ); } @@ -365,20 +370,26 @@ public function morphToMany($related, $name, $table = null, $foreignKey = null, * @param string $related * @param string $name * @param string $table - * @param string $foreignKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey + * @param string $parentKey * @param string $relatedKey * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ - public function morphedByMany($related, $name, $table = null, $foreignKey = null, $relatedKey = null) + public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null, + $relatedPivotKey = null, $parentKey = null, $relatedKey = null) { - $foreignKey = $foreignKey ?: $this->getForeignKey(); + $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey(); // For the inverse of the polymorphic many-to-many relations, we will change // the way we determine the foreign and other keys, as it is the opposite // of the morph-to-many method since we're figuring out these inverses. - $relatedKey = $relatedKey ?: $name.'_id'; + $relatedPivotKey = $relatedPivotKey ?: $name.'_id'; - return $this->morphToMany($related, $name, $table, $foreignKey, $relatedKey, true); + return $this->morphToMany( + $related, $name, $table, $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, true + ); } /** diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 93137325c534..64cf0dd2123d 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -25,14 +25,14 @@ class BelongsToMany extends Relation * * @var string */ - protected $foreignKey; + protected $foreignPivotKey; /** * The associated key of the relation. * * @var string */ - protected $relatedKey; + protected $relatedPivotKey; /** * The key name of the parent model. @@ -46,7 +46,7 @@ class BelongsToMany extends Relation * * @var string */ - protected $localKey; + protected $relatedKey; /** * The "name" of the relationship. @@ -117,15 +117,15 @@ class BelongsToMany extends Relation * @param string $relationName * @return void */ - public function __construct(Builder $query, Model $parent, $table, $foreignKey, - $relatedKey, $parentKey, $localKey, $relationName = null) + public function __construct(Builder $query, Model $parent, $table, $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relationName = null) { $this->table = $table; - $this->localKey = $localKey; $this->parentKey = $parentKey; $this->relatedKey = $relatedKey; - $this->foreignKey = $foreignKey; $this->relationName = $relationName; + $this->relatedPivotKey = $relatedPivotKey; + $this->foreignPivotKey = $foreignPivotKey; parent::__construct($query, $parent); } @@ -159,9 +159,9 @@ protected function performJoin($query = null) // model instance. Then we can set the "where" for the parent models. $baseTable = $this->related->getTable(); - $key = $baseTable.'.'.$this->localKey; + $key = $baseTable.'.'.$this->relatedKey; - $query->join($this->table, $key, '=', $this->getQualifiedRelatedKeyName()); + $query->join($this->table, $key, '=', $this->getQualifiedRelatedPivotKeyName()); return $this; } @@ -174,7 +174,7 @@ protected function performJoin($query = null) protected function addWhereConstraints() { $this->query->where( - $this->getQualifiedForeignKeyName(), '=', $this->parent->{$this->parentKey} + $this->getQualifiedForeignPivotKeyName(), '=', $this->parent->{$this->parentKey} ); return $this; @@ -188,7 +188,7 @@ protected function addWhereConstraints() */ public function addEagerConstraints(array $models) { - $this->query->whereIn($this->getQualifiedForeignKeyName(), $this->getKeys($models)); + $this->query->whereIn($this->getQualifiedForeignPivotKeyName(), $this->getKeys($models)); } /** @@ -247,7 +247,7 @@ protected function buildDictionary(Collection $results) $dictionary = []; foreach ($results as $result) { - $dictionary[$result->pivot->{$this->foreignKey}][] = $result; + $dictionary[$result->pivot->{$this->foreignPivotKey}][] = $result; } return $dictionary; @@ -540,7 +540,7 @@ protected function shouldSelect(array $columns = ['*']) */ protected function aliasedPivotColumns() { - $defaults = [$this->foreignKey, $this->relatedKey]; + $defaults = [$this->foreignPivotKey, $this->relatedPivotKey]; return collect(array_merge($defaults, $this->pivotColumns))->map(function ($column) { return $this->table.'.'.$column.' as pivot_'.$column; @@ -840,7 +840,7 @@ public function getRelationExistenceQueryForSelfJoin(Builder $query, Builder $pa */ public function getExistenceCompareKey() { - return $this->getQualifiedForeignKeyName(); + return $this->getQualifiedForeignPivotKeyName(); } /** @@ -893,9 +893,9 @@ public function updatedAt() * * @return string */ - public function getQualifiedForeignKeyName() + public function getQualifiedForeignPivotKeyName() { - return $this->table.'.'.$this->foreignKey; + return $this->table.'.'.$this->foreignPivotKey; } /** @@ -903,9 +903,9 @@ public function getQualifiedForeignKeyName() * * @return string */ - public function getQualifiedRelatedKeyName() + public function getQualifiedRelatedPivotKeyName() { - return $this->table.'.'.$this->relatedKey; + return $this->table.'.'.$this->relatedPivotKey; } /** diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php index 98180a7af6a8..e74e3cf205b5 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php @@ -29,7 +29,7 @@ public function toggle($ids, $touch = true) // checking which of the given ID/records is in the list of current records // and removing all of those rows from this "intermediate" joining table. $detach = array_values(array_intersect( - $this->newPivotQuery()->pluck($this->relatedKey)->all(), + $this->newPivotQuery()->pluck($this->relatedPivotKey)->all(), array_keys($records) )); @@ -89,7 +89,7 @@ public function sync($ids, $detaching = true) // in this joining table. We'll spin through the given IDs, checking to see // if they exist in the array of current ones, and if not we will insert. $current = $this->newPivotQuery()->pluck( - $this->relatedKey + $this->relatedPivotKey )->all(); $detach = array_diff($current, array_keys( @@ -287,9 +287,9 @@ protected function extractAttachIdAndAttributes($key, $value, array $attributes) */ protected function baseAttachRecord($id, $timed) { - $record[$this->relatedKey] = $id; + $record[$this->relatedPivotKey] = $id; - $record[$this->foreignKey] = $this->parent->getKey(); + $record[$this->foreignPivotKey] = $this->parent->getKey(); // If the record needs to have creation and update timestamps, we will make // them by calling the parent model's "freshTimestamp" method which will @@ -353,7 +353,7 @@ public function detach($ids = null, $touch = true) return 0; } - $query->whereIn($this->relatedKey, (array) $ids); + $query->whereIn($this->relatedPivotKey, (array) $ids); } // Once we have all of the conditions set on the statement, we are ready @@ -381,7 +381,7 @@ public function newPivot(array $attributes = [], $exists = false) $this->parent, $attributes, $this->table, $exists, $this->using ); - return $pivot->setPivotKeys($this->foreignKey, $this->relatedKey); + return $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey); } /** @@ -413,7 +413,7 @@ public function newPivotStatement() */ public function newPivotStatementForId($id) { - return $this->newPivotQuery()->where($this->relatedKey, $id); + return $this->newPivotQuery()->where($this->relatedPivotKey, $id); } /** @@ -433,7 +433,7 @@ protected function newPivotQuery() call_user_func_array([$query, 'whereIn'], $arguments); } - return $query->where($this->foreignKey, $this->parent->getKey()); + return $query->where($this->foreignPivotKey, $this->parent->getKey()); } /** diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php index 865eb37e1352..9d14c4471dc1 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php @@ -38,22 +38,25 @@ class MorphToMany extends BelongsToMany * @param \Illuminate\Database\Eloquent\Model $parent * @param string $name * @param string $table - * @param string $foreignKey - * @param string $relatedKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey * @param string $parentKey - * @param string $localKey + * @param string $relatedKey * @param string $relationName * @param bool $inverse * @return void */ - public function __construct(Builder $query, Model $parent, $name, $table, $foreignKey, - $relatedKey, $parentKey, $localKey, $relationName = null, $inverse = false) + public function __construct(Builder $query, Model $parent, $name, $table, $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relationName = null, $inverse = false) { $this->inverse = $inverse; $this->morphType = $name.'_type'; $this->morphClass = $inverse ? $query->getModel()->getMorphClass() : $parent->getMorphClass(); - parent::__construct($query, $parent, $table, $foreignKey, $relatedKey, $parentKey, $localKey, $relationName); + parent::__construct( + $query, $parent, $table, $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relationName + ); } /** @@ -136,7 +139,7 @@ public function newPivot(array $attributes = [], $exists = false) $pivot = $using ? $using::fromRawAttributes($this->parent, $attributes, $this->table, $exists) : new MorphPivot($this->parent, $attributes, $this->table, $exists); - $pivot->setPivotKeys($this->foreignKey, $this->relatedKey) + $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey) ->setMorphType($this->morphType) ->setMorphClass($this->morphClass); diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index 0dbf655e32fb..9a3307e11cef 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -1015,8 +1015,8 @@ public function testBelongsToManyCreatesProperRelation() $this->addMockConnection($model); $relation = $model->belongsToMany('Illuminate\Tests\Database\EloquentModelSaveStub'); - $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_stub_id', $relation->getQualifiedForeignKeyName()); - $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_save_stub_id', $relation->getQualifiedRelatedKeyName()); + $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_stub_id', $relation->getQualifiedForeignPivotKeyName()); + $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_save_stub_id', $relation->getQualifiedRelatedPivotKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf('Illuminate\Tests\Database\EloquentModelSaveStub', $relation->getQuery()->getModel()); $this->assertEquals(__FUNCTION__, $relation->getRelationName()); @@ -1024,8 +1024,8 @@ public function testBelongsToManyCreatesProperRelation() $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->belongsToMany('Illuminate\Tests\Database\EloquentModelSaveStub', 'table', 'foreign', 'other'); - $this->assertEquals('table.foreign', $relation->getQualifiedForeignKeyName()); - $this->assertEquals('table.other', $relation->getQualifiedRelatedKeyName()); + $this->assertEquals('table.foreign', $relation->getQualifiedForeignPivotKeyName()); + $this->assertEquals('table.other', $relation->getQualifiedRelatedPivotKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf('Illuminate\Tests\Database\EloquentModelSaveStub', $relation->getQuery()->getModel()); }