Permalink
Browse files

BelongsTo relationships may now reference any key on parent model, no…

…t just primary key.
  • Loading branch information...
taylorotwell committed Oct 31, 2013
1 parent da117f1 commit c7d2740404251d9e08a24eacbbc42812b038fbaa
@@ -585,9 +585,10 @@ public function morphOne($related, $name, $type = null, $id = null)
*
* @param string $related
* @param string $foreignKey
* @param string $otherKey
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function belongsTo($related, $foreignKey = null)
public function belongsTo($related, $foreignKey = null, $otherKey = null)
{
list(, $caller) = debug_backtrace(false);
@@ -601,14 +602,16 @@ public function belongsTo($related, $foreignKey = null)
$foreignKey = snake_case($relation).'_id';
}
$instance = new $related;
// Once we have the foreign key names, we'll just create a new Eloquent query
// for the related models and returns the relationship instance which will
// actually be responsible for retrieving and hydrating every relations.
$instance = new $related;
$query = $instance->newQuery();
return new BelongsTo($query, $this, $foreignKey, $relation);
$otherKey = $otherKey ?: $instance->getKeyName();
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
/**
@@ -14,6 +14,13 @@ class BelongsTo extends Relation {
*/
protected $foreignKey;
/**
* The associated key on the parent model.
*
* @var string
*/
protected $otherKey;
/**
* The name of the relationship.
*
@@ -27,11 +34,13 @@ class BelongsTo extends Relation {
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $relation)
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $relation)
{
$this->otherKey = $otherKey;
$this->relation = $relation;
$this->foreignKey = $foreignKey;
@@ -60,11 +69,9 @@ public function addConstraints()
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$key = $this->related->getKeyName();
$table = $this->related->getTable();
$this->query->where($table.'.'.$key, '=', $this->parent->{$this->foreignKey});
$this->query->where($table.'.'.$this->otherKey, '=', $this->parent->{$this->foreignKey});
}
}
@@ -90,9 +97,7 @@ public function addEagerConstraints(array $models)
// We'll grab the primary key name of the related models since it could be set to
// a non-standard name and not "id". We will then construct the constraint for
// our eagerly loading query so it returns the proper models from execution.
$key = $this->related->getKeyName();
$key = $this->related->getTable().'.'.$key;
$key = $this->related->getTable().'.'.$this->otherKey;
$this->query->whereIn($key, $this->getEagerModelKeys($models));
}
@@ -158,14 +163,16 @@ public function match(array $models, Collection $results, $relation)
{
$foreign = $this->foreignKey;
$other = $this->otherKey;
// First we will get to build a dictionary of the child models by their primary
// key of the relationship, then we can easily match the children back onto
// the parents using that dictionary and the primary key of the children.
$dictionary = array();
foreach ($results as $result)
{
$dictionary[$result->getKey()] = $result;
$dictionary[$result->getAttribute($other)] = $result;
}
// Once we have the dictionary constructed, we can loop through all the parents
@@ -190,7 +197,7 @@ public function match(array $models, Collection $results, $relation)
*/
public function associate(Model $model)
{
$this->parent->setAttribute($this->foreignKey, $model->getKey());
$this->parent->setAttribute($this->foreignKey, $model->getAttribute($this->otherKey));
return $this->parent->setRelation($this->relation, $model);
}
@@ -36,7 +36,8 @@
{"message": "Allow for passing of custom attributes into Validator::make as fourth parameter.", "backport": null},
{"message": "Allow comma delimited list of queues to be passed to queue:listen / queue:work to implement queue priority.", "backport": null},
{"message": "When new bindings are added to container, old aliases bound to that key will now be dropped.", "backport": null},
{"message": "Added new 'resolvable' and 'isAlias' methods to the container.", "backport": null}
{"message": "Added new 'resolvable' and 'isAlias' methods to the container.", "backport": null},
{"message": "BelongsTo relationships may now reference any key on parent model, not just primary key.", "backport": null}
],
"4.0.x": [
{"message": "Added implode method to query builder and Collection class.", "backport": null},
@@ -48,17 +48,17 @@ public function testModelsAreProperlyMatchedToParents()
{
$relation = $this->getRelation();
$result1 = m::mock('stdClass');
$result1->shouldReceive('getKey')->andReturn(1);
$result1->shouldReceive('getAttribute')->with('id')->andReturn(1);
$result2 = m::mock('stdClass');
$result2->shouldReceive('getKey')->andReturn(2);
$result2->shouldReceive('getAttribute')->with('id')->andReturn(2);
$model1 = new EloquentBelongsToModelStub;
$model1->foreign_key = 1;
$model2 = new EloquentBelongsToModelStub;
$model2->foreign_key = 2;
$models = $relation->match(array($model1, $model2), new Collection(array($result1, $result2)), 'foo');
$this->assertEquals(1, $models[0]->foo->getKey());
$this->assertEquals(2, $models[1]->foo->getKey());
$this->assertEquals(1, $models[0]->foo->getAttribute('id'));
$this->assertEquals(2, $models[1]->foo->getAttribute('id'));
}
@@ -68,7 +68,7 @@ public function testAssociateMethodSetsForeignKeyOnModel()
$parent->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn('foreign.value');
$relation = $this->getRelation($parent);
$associate = m::mock('Illuminate\Database\Eloquent\Model');
$associate->shouldReceive('getKey')->once()->andReturn(1);
$associate->shouldReceive('getAttribute')->once()->with('id')->andReturn(1);
$parent->shouldReceive('setAttribute')->once()->with('foreign_key', 1);
$parent->shouldReceive('setRelation')->once()->with('relation', $associate);
@@ -85,7 +85,7 @@ protected function getRelation($parent = null)
$related->shouldReceive('getTable')->andReturn('relation');
$builder->shouldReceive('getModel')->andReturn($related);
$parent = $parent ?: new EloquentBelongsToModelStub;
return new BelongsTo($builder, $parent, 'foreign_key', 'relation');
return new BelongsTo($builder, $parent, 'foreign_key', 'id', 'relation');
}
}

0 comments on commit c7d2740

Please sign in to comment.