diff --git a/src/Relations/BelongsToThrough.php b/src/Relations/BelongsToThrough.php index ccc11bb..94d378b 100644 --- a/src/Relations/BelongsToThrough.php +++ b/src/Relations/BelongsToThrough.php @@ -17,6 +17,7 @@ */ class BelongsToThrough extends Relation { + /** * Column alias for matching eagerly loaded models. * @@ -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); } @@ -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]); @@ -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); @@ -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) @@ -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) @@ -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) @@ -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; @@ -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) @@ -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) @@ -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]); } } @@ -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; diff --git a/src/Traits/BelongsToThrough.php b/src/Traits/BelongsToThrough.php index bc7938d..5dbd665 100644 --- a/src/Traits/BelongsToThrough.php +++ b/src/Traits/BelongsToThrough.php @@ -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.'); @@ -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); } } diff --git a/tests/BelongsToThroughTest.php b/tests/BelongsToThroughTest.php index 3059595..a2d4a10 100644 --- a/tests/BelongsToThroughTest.php +++ b/tests/BelongsToThroughTest.php @@ -9,6 +9,7 @@ */ class BelongsToThroughTest extends \Orchestra\Testbench\TestCase { + /** * Define environment setup. * @@ -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'; @@ -72,6 +82,7 @@ public function getForeignKey() class Stub_Test_Model_Contient extends Stub_Parent_Model { + protected $table = 'continents'; public function countries() @@ -82,6 +93,7 @@ public function countries() class Stub_Test_Model_Country extends Stub_Parent_Model { + protected $table = 'countries'; public function continent() @@ -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'; @@ -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_'); } } diff --git a/tests/database.sqlite b/tests/database.sqlite index fb47d60..5ff959b 100644 Binary files a/tests/database.sqlite and b/tests/database.sqlite differ