The 5.1 version of HasManyThrough is limited; you cannot specify the key on the final table that connects it to the intermediate table.
If you’re unfortunately stuck on 5.1 but want this functionality, the following HasManyThrough should help. Tested with normally loading the relationship (ie modelInstance->relationship
) and using with
(ie model::with('relationship')->where()
), not yet with has
or whereHas
.
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
/**
* A custom version of HasManyThrough upgrading Eloquent 5.1 HasManyThrough to the 5.5 version.
* This allows us to specify the key on the intermediate table connecting to the final table.
*/
class CustomHasManyThrough extends HasManyThrough {
/**
* The "through" parent model instance.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $throughParent;
/**
* The local key on the intermediary model.
*
* @var string
*/
protected $secondLocalKey;
/**
* Create a new has many through relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $farParent
* @param \Illuminate\Database\Eloquent\Model $throughParent
* @param string $firstKey
* @param string $secondKey
* @param string $localKey
* @param string $secondLocalKey
* @return void
*/
public function __construct($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey){
$this->localKey = $localKey;
$this->firstKey = $firstKey;
$this->secondKey = $secondKey;
$this->secondLocalKey = $secondLocalKey;
$this->farParent = $farParent;
$this->throughParent = $throughParent;
Relation::__construct($query, $throughParent);
}
/**
* Get the fully qualified parent key name.
*
* @return string
*/
public function getQualifiedParentKeyName(){
return $this->parent->getTable().'.'.$this->secondLocalKey;
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$table = $this->parent->getTable();
$this->query->whereIn($table.'.'.$this->firstKey, $this->getKeys($models, $this->localKey));
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, \Illuminate\Database\Eloquent\Collection $results, $relation)
{
$dictionary = $this->buildDictionary($results);
// Once we have the dictionary we can simply spin through the parent models to
// link them up with their children using the keyed dictionary to make the
// matching very convenient and easy work. Then we'll just return them.
foreach ($models as $model) {
$key = $model->getAttribute($this->localKey);
if (isset($dictionary[$key])) {
$value = $this->related->newCollection($dictionary[$key]);
$model->setRelation($relation, $value);
}
}
return $models;
}
}
class CustomEloquent extends Eloquent {
/**
* Replace the Eloquent has-many-through relationship with our custom one.
*
* @param string $related
* @param string $through
* @param string|null $firstKey
* @param string|null $secondKey
* @param string|null $localKey
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null){
$through = new $through;
$firstKey = $firstKey ?: $this->getForeignKey();
$secondKey = $secondKey ?: $through->getForeignKey();
$localKey = $localKey ?: $this->getKeyName();
$secondLocalKey ?: $through->getKeyName();
return new CustomHasManyThrough((new $related)->newQuery(), $this, $through, $firstKey, $secondKey, $localKey, $secondLocalKey);
}
}