Skip to content

Commit

Permalink
Prefixed foreign key in last model of deep BelongToThrough relation
Browse files Browse the repository at this point in the history
  • Loading branch information
znck committed Dec 10, 2015
1 parent ff9d9c8 commit 03f7b40
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 25 deletions.
48 changes: 29 additions & 19 deletions src/Relations/BelongsToThrough.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
class BelongsToThrough extends Relation
{

/**
* Column alias for matching eagerly loaded models.
*
Expand All @@ -38,18 +39,25 @@ class BelongsToThrough extends Relation
*/
protected $localKey;

/**
* @var string
*/
private $prefix;

/**
* Create a new instance of relation.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param array $models
* @param string|null $localKey
* @param \Illuminate\Database\Eloquent\Model $parent
* @param array $models
* @param string|null $localKey
* @param string $prefix
*/
public function __construct(Builder $query, Model $parent, array $models, $localKey = null)
public function __construct(Builder $query, Model $parent, array $models, $localKey = null, $prefix = '')
{
$this->models = $models;
$this->localKey = $localKey ?: $parent->getKeyName();
$this->prefix = $prefix;

parent::__construct($query, $parent);
}
Expand All @@ -61,7 +69,7 @@ public function addConstraints()
{
$this->setJoins();

$this->getQuery()->select([$this->getRelated()->getTable().'.*']);
$this->getQuery()->select([ $this->getRelated()->getTable().'.*' ]);

if (static::$constraints) {
$this->getQuery()->where($this->getQualifiedParentKeyName(), '=', $this->parent[$this->localKey]);
Expand All @@ -76,7 +84,11 @@ protected function setJoins()
{
$one = $this->getRelated()->getQualifiedKeyName();
$prev = $this->getForeignKey($this->getRelated());
foreach ($this->models as $model) {
$lastIndex = count($this->models) - 1;
foreach ($this->models as $index => $model) {
if ($lastIndex === $index) {
$prev = $this->prefix.$prev;
}
$other = $model->getTable().'.'.$prev;
$this->getQuery()->leftJoin($model->getTable(), $one, '=', $other);

Expand Down Expand Up @@ -111,7 +123,6 @@ protected function setSoftDeletes()
* Get foreign key column name for the model.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return string
*/
protected function getForeignKey(Model $model)
Expand All @@ -123,7 +134,6 @@ protected function getForeignKey(Model $model)
* Determine whether the model uses Soft Deletes.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return bool
*/
public function hasSoftDeletes(Model $model)
Expand All @@ -150,7 +160,6 @@ public function addEagerConstraints(array $models)
*
* @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $parent
*
* @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
Expand All @@ -160,7 +169,11 @@ public function getRelationCountQuery(Builder $query, Builder $parent)
$one = $this->getRelated()->getQualifiedKeyName();
$prev = $this->getForeignKey($this->getRelated());
$alias = null;
foreach ($this->models as $model) {
$lastIndex = count($this->models);
foreach ($this->models as $index => $model) {
if ($lastIndex === $index) {
$prev = $this->prefix.$prev;
}
if ($this->getParent()->getTable() == $model->getTable()) {
$alias = $model->getTable().'_'.time();
$other = $alias.'.'.$prev;
Expand Down Expand Up @@ -195,8 +208,7 @@ public function getResults()
* Initialize the relation on a set of models.
*
* @param \Illuminate\Database\Eloquent\Model[] $models
* @param string $relation
*
* @param string $relation
* @return array
*/
public function initRelation(array $models, $relation)
Expand All @@ -211,10 +223,9 @@ public function initRelation(array $models, $relation)
/**
* Match the eagerly loaded results to their parents.
*
* @param \Illuminate\Database\Eloquent\Model[] $models
* @param \Illuminate\Database\Eloquent\Model[] $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
*
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
Expand All @@ -224,7 +235,7 @@ public function match(array $models, Collection $results, $relation)
foreach ($models as $model) {
$key = $model->{$this->localKey};

if (isset($dictionary[$key])) {
if (isset( $dictionary[$key] )) {
$model->setRelation($relation, $dictionary[$key]);
}
}
Expand All @@ -236,16 +247,15 @@ public function match(array $models, Collection $results, $relation)
* Build model dictionary keyed by the relation's foreign key.
*
* @param \Illuminate\Database\Eloquent\Collection $results
*
* @return array
*/
protected function buildDictionary(Collection $results)
{
$dictionary = [];
$dictionary = [ ];

foreach ($results as $result) {
$dictionary[$result->{static::RELATED_THROUGH_KEY}] = $result;
unset($result[static::RELATED_THROUGH_KEY]);
unset( $result[static::RELATED_THROUGH_KEY] );
}

return $dictionary;
Expand Down
12 changes: 7 additions & 5 deletions src/Traits/BelongsToThrough.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
*/
trait BelongsToThrough
{

/**
* Define a belongs-to-through relationship.
*
* @param string $related
* @param string $related
* @param string|array $through
* @param string|null $localKey
*
* @param string|null $localKey Primary Key (Default: id)
* @param string $prefix Foreign key prefix
* @return \Znck\Eloquent\Relations\BelongsToThrough
* @throws \Exception
*/
public function belongsToThrough($related, $through, $localKey = null)
public function belongsToThrough($related, $through, $localKey = null, $prefix = '')
{
if (!$this instanceof Model) {
throw new Exception('belongsToThrough can used on '.Model::class.' only.');
Expand All @@ -46,6 +48,6 @@ public function belongsToThrough($related, $through, $localKey = null)

$models[] = $this;

return new Relation($relatedModel->newQuery(), $this, $models, $localKey);
return new Relation($relatedModel->newQuery(), $this, $models, $localKey, $prefix);
}
}
24 changes: 23 additions & 1 deletion tests/BelongsToThroughTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
class BelongsToThroughTest extends \Orchestra\Testbench\TestCase
{

/**
* Define environment setup.
*
Expand Down Expand Up @@ -60,10 +61,19 @@ public function test_has_relation()
$this->assertCount(16, $cities_with_country);
$this->assertCount(18, $all_cities);
}

public function test_prefixed_foreign_key()
{
$city = Stub_Test_Model_City::where('id', 1)->first();

$this->assertNotNull($city->otherCountry);
$this->assertEquals(1, $city->otherCountry->id);
}
}

class Stub_Parent_Model extends Eloquent
{

public function getForeignKey()
{
return Str::singular($this->getTable()).'_id';
Expand All @@ -72,6 +82,7 @@ public function getForeignKey()

class Stub_Test_Model_Contient extends Stub_Parent_Model
{

protected $table = 'continents';

public function countries()
Expand All @@ -82,6 +93,7 @@ public function countries()

class Stub_Test_Model_Country extends Stub_Parent_Model
{

protected $table = 'countries';

public function continent()
Expand All @@ -92,11 +104,13 @@ public function continent()

class Stub_Test_Model_State extends Stub_Parent_Model
{

protected $table = 'states';
}

class Stub_Test_Model_District extends Stub_Parent_Model
{

use BelongsToThrough;

protected $table = 'districts';
Expand All @@ -109,12 +123,20 @@ public function country()

class Stub_Test_Model_City extends Stub_Parent_Model
{

use BelongsToThrough;

protected $table = 'cities';

public function country()
{
return $this->belongsToThrough(Stub_Test_Model_Country::class, [Stub_Test_Model_State::class, Stub_Test_Model_District::class]);
return $this->belongsToThrough(Stub_Test_Model_Country::class,
[ Stub_Test_Model_State::class, Stub_Test_Model_District::class ]);
}

public function otherCountry()
{
return $this->belongsToThrough(Stub_Test_Model_Country::class,
[ Stub_Test_Model_State::class, Stub_Test_Model_District::class ], null, 'other_');
}
}
Binary file modified tests/database.sqlite
Binary file not shown.

0 comments on commit 03f7b40

Please sign in to comment.