Skip to content

Commit

Permalink
fix various problems with belongs to many (#18380)
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell authored Mar 16, 2017
1 parent bb47918 commit 3aa2adc
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 57 deletions.
51 changes: 31 additions & 20 deletions src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
);
}

Expand All @@ -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();

Expand All @@ -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
Expand All @@ -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
);
}

Expand All @@ -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
);
}

/**
Expand Down
36 changes: 18 additions & 18 deletions src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -46,7 +46,7 @@ class BelongsToMany extends Relation
*
* @var string
*/
protected $localKey;
protected $relatedKey;

/**
* The "name" of the relationship.
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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));
}

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -840,7 +840,7 @@ public function getRelationExistenceQueryForSelfJoin(Builder $query, Builder $pa
*/
public function getExistenceCompareKey()
{
return $this->getQualifiedForeignKeyName();
return $this->getQualifiedForeignPivotKeyName();
}

/**
Expand Down Expand Up @@ -893,19 +893,19 @@ public function updatedAt()
*
* @return string
*/
public function getQualifiedForeignKeyName()
public function getQualifiedForeignPivotKeyName()
{
return $this->table.'.'.$this->foreignKey;
return $this->table.'.'.$this->foreignPivotKey;
}

/**
* Get the fully qualified "related key" for the relation.
*
* @return string
*/
public function getQualifiedRelatedKeyName()
public function getQualifiedRelatedPivotKeyName()
{
return $this->table.'.'.$this->relatedKey;
return $this->table.'.'.$this->relatedPivotKey;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
));

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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());
}

/**
Expand Down
17 changes: 10 additions & 7 deletions src/Illuminate/Database/Eloquent/Relations/MorphToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}

/**
Expand Down Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions tests/Database/DatabaseEloquentModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1015,17 +1015,17 @@ 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());

$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());
}
Expand Down

0 comments on commit 3aa2adc

Please sign in to comment.