Это должно быть приписано Джошуа Двайру, когда он указал мне путь к этому решению. Меня заинтриговало его упоминание о расширении стандартного класса BelongsTo
и о том, чтобы он работал на меня. В идеале я хочу иметь возможность вызывать пользовательские отношения:
$this->belongsToBook('\App\Book');
И чтобы эта функция возвращала правильно приведенный объект.
Маршрутизируя код, я обнаружил, что именно черта HasRelationship
, используемая Model
, была ответственна за возвращение отношений. Изменяя это отношение, мы можем изменить реализацию и, следовательно, возвращаемый объект.
Я также хотел воспроизвести ту же методологию, которую использует Laravel, поэтому подражал ей в своем собственном приложении.
Учитывая все это, первым шагом является создание новой черты HasBookRelationship
, которую можно использовать в модели для обработки вызова к $this->belongsToBook('\App\Book')
:
trait HasBookRelationship
{
public function belongsToBook($related, $foreignKey = null, $ownerKey = null, $relation = null)
{
if (is_null($relation)) {
$relation = $this->guessBelongsToRelation();
}
$instance = $this->newRelatedInstance($related);
if (is_null($foreignKey)) {
$foreignKey = \Str::snake($relation).'_'.$instance->getKeyName();
}
$ownerKey = $ownerKey ?: $instance->getKeyName();
//We change the return relationship here
**return new BelongsToBook(
$instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
);**
}
}
Это просто скопировано из существующего belongsTo
метода в черте HasRelationships
. Ключевым моментом здесь является то, что мы собираемся вернуть пользовательские отношения BelongsToBook
и использовать их для переопределения возвращаемого значения. Последняя строка метода изменяется, чтобы вернуть желаемый класс отношений.
Используемый нами класс расширен с BelongsTo
, но мы изменили метод get
для приведения объекта перед его возвратом.
class BelongsToBook extends BelongsTo
{
public function __construct(Builder $query, Model $child, $foreignKey, $ownerKey, $relationName)
{
parent::__construct($query, $child, $foreignKey, $ownerKey, $relationName);
}
public function get($columns = ['*'])
{
$objs = $this->query->get($columns);
//iterate over the collated objects...
$objs->transform(function($item)
{
//..and return a cast object with whatever method you want
return castTheCorrectObject($item);
});
return $objs;
}
}
castTheCorrectObject
может быть любой понравившейся вам функцией приведения, возможно, настроенной в качестве помощника или другого метода в отношениях.
Как только они настроены, мы можем использовать их в нашей собственной модели:
class Author extends Model
{
use HasBookRelationship;
public function books()
{
return $this->belongsToBook('\App\Book');
}
}
Это вернет коллекцию правильно отлитых объектов и сохранит связь.
Одна вещь меня озадачила. Метод, который я преодолел в своем BelongsToBook
классе, был get()
, а не getResults()
, как предложил Иисус Навин. get()
определяется в Relation
и наследуется BelongsTo
, где getResults()
определяется в BelongsTo
. Я не уверен, в чем разница между getResults()
и get()
и почему мне пришлось переопределить get()
вместо getResults()
. Если кто-то может пролить свет, это будет оценено.