Преобразование в унаследованный класс с помощью Eloquent Realtionship - PullRequest
1 голос
/ 27 марта 2019

Я хотел бы спросить об этом, поскольку Laravel - самый элегантный фреймворк, с которым я когда-либо сталкивался, и задавался вопросом, был ли "более красивый" способ сделать это.

У меня есть система, которая записывает книги так:

class Chapter extends Model
{
    public function book()
    {
        return $this->belongsTo('\App\Book');
    }
}

В системе есть ряд других моделей, которые простираются от «Книги», таких как «Роман», «Биография» и т. Д. Есть ли способ для Eloquent предоставить мне правильно приведенный объект, учитывая правильную информацию (т.е. Пространство имен) В настоящее время я получаю книгу и разыгрываю ее, используя функцию https://gist.github.com/borzilleri/960035, которая работает, но не очень аккуратно.

Ответы [ 2 ]

2 голосов
/ 27 марта 2019

Я вижу несколько разных вариантов здесь.Можно было бы написать свой класс следующим образом:

class Chapter extends Model
{
    public function book()
    {
        return $this->belongsTo('\App\Book');
    }

    public function biography()
    {
        return $this->belongsTo('\App\Biography')->where('type', 'biography');
    }

    public function novel()
    {
        return $this->belongsTo('\App\Novel')->where('type', 'novel');
    }
}

Тогда вам нужно будет заранее знать, какой это тип книги.Другим было бы сделать что-то вроде этого:

class Chapter extends Model
{
    protected function parent_book()
    {
        return $this->belongsTo('\App\Book');
    }

    public function getBookAttribute()
    {
        $book = $this->parent_book;
        if (!$book) return $book; // No related book.

        if ($book->type == 'novel') return (Novel)$book;
        if ($book->type == 'biography') return (Biography)$book;

        return $book;
    }
}

Вы по-прежнему должны выполнять кастинг самостоятельно, но, по крайней мере, все это в одном месте и прозрачно для остальной части приложения, поскольку оно все еще можетпросто ссылка $chapter->book Для этого второго решения, если вы когда-нибудь установите $chapter->book = new Book(), вам также необходимо убедиться, что вы сделали функцию setBookAttribute().

Еще одна сложная возможность - создать свой собственный.пользовательский тип отношения, расширяя класс BelongsTo и переопределяя getResults() на кастинг перед возвратом результата.Это было бы довольно прозрачно снаружи и позволяло бы вам по-прежнему вызывать $ chapter-> book () и рассматривать его как отношение.

1 голос
/ 28 марта 2019

Это должно быть приписано Джошуа Двайру, когда он указал мне путь к этому решению. Меня заинтриговало его упоминание о расширении стандартного класса 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(). Если кто-то может пролить свет, это будет оценено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...