Наследование модели в Laravel

В настоящее время я работаю с Laravel, и я борюсь с тем, что каждая модель должна простираться от Eloquent, и я не уверен, как реализовать иерархию модели (с разными таблицами).

Пример:

Скажем, у меня есть абстрактная модель Tool, а затем модель Hammer и модель Screwdriver, которые простираются от инструмента.

Теперь инструмент будет расширять Eloquent... НО, нет таблицы для инструментов, есть таблица для Hammers и другая таблица для отверток, потому что они имеют разные атрибуты.

Как указать, что у Hammer есть таблица, и Screwdriver имеет таблицу, когда они оба расширяют Tool? И как я могу использовать Eloquent для вызова, например, для всех инструментов?

Вроде:

Tools::all()

Это должно привести все молотки и отвертки, потому что они все Инструменты

Возможно ли использование Eloquent?

Ответы

Ответ 1

Несмотря на то, что мне кажется, что подход "Полиморфные отношения" (PR) немного лучше, чем "Единственное наследование таблицы" (STI), он по-прежнему не чувствует ничего похожего на подход, основанный на истинном наследовании, Из точки зрения домена (например, диаграммы класса UML) это похоже на попытку использования отношения композиции вместо наследования.

С точки зрения согласованности базы данных полиморфное отношение само по себе уже является очень странным решением в Laravel (IMHO). Поскольку не может быть ни одного поля, являющегося внешним ключом для нескольких таблиц, это может привести к объединению идентификатора, который не должен быть соединен. И обратные отношения, которые не соблюдаются.

Хотя я на самом деле не описываю решение проблемы, я бы предложил другой подход, чем PR и STI. Это решение будет похоже на подход Hibernate table-per-subclass. Я думаю, что Dauce extension to Eloquent идет в том же направлении, за исключением, кажется, еще нескольких проблем с реализацией.

С точки зрения базы данных таблица для каждого подкласса также означает, что суперкласс содержит один столбец для каждого прямого подкласса. Затем вы можете установить ограничения внешнего ключа на (не нулевой) id и правильно использовать отношения. Однако в классе Laravel magic Eloquent все еще должна быть какая-то дополнительная логика, которая превращает запрошенный объект в правильный тип.

Итак, для меня функционально модели Eloquent должны правильно наследоваться на стороне PHP, в то время как база данных все еще может использовать ограничения внешнего ключа.

Ответ 2

Примечание. Вы не можете использовать абстрактный класс напрямую, он должен быть расширен дочерним классом

Если ваша модель Tool (abstract) не имеет сопоставленного с ней table, вам не нужно использовать Tool::all, и вы не можете напрямую использовать/создавать экземпляр модели abstract но вы можете использовать эту модель abstract в качестве базового класса следующим образом:

abstract class Tool extends Eloquent {

    protected $validator = null;
    protected $errors = null;
    protected $rules = array();

    // Declare common methods here that
    // will be used by both child models, for example:

    public static function boot()
    {
        parent::boot();
        static::saving(function($model)
        {
            if(!$this->isvalid($model->toArray(), $this->rules) return false;
        });
    }

    protected function isvalid($inputs, $rules)
    {
        // return true/false
        $this->validator = Validator::make($inputs, $rules);
        if($this->validator->passes()) return true;
        else {
            $this->errors = $this->validator->errors();
            return false;
        }
    }

    public function getErrors()
    {
        return $this->errors;
    }

    public function hasErrors()
    {
        return count($this->errors);
    }

    // declare any abstract method (method header only)
    // that every child model needs to implement individually
}

class Hammer extends Tool {
    protected $table = 'hammers';
    protected $fillable = array(...);
    protected $rules = array(...); // Declare own rules

}

class Screwdriver extends Tool {
    protected $table = 'screwdrivers';
    protected $fillable = array(...);
    protected $rules = array(...); // Declare own rules
}

Используйте Hammer и Screwdriver напрямую, но никогда не Tool model/class, потому что это класс abstract, например:

$hammers = Hammer:all();

Или может быть что-то вроде этого:

$screwdriver = Screwdriver:create(Input::all());
if($screwdrivers->hasErrors()) {
    return Redirect::back()->withInput()->withErrors($screwdriver->getErrors());
}
return Redirect::route('Screwdriver.index');

Ответ 3

Я нашел ошибку (функция?), где Laravel 5 просматривает неправильный класс, если ParentClass и ChildClass имеют одну и ту же строку $table. Поэтому, если вы вызываете ParentClass:: all() в определенных ситуациях, он может возвращать экземпляры ChildClass в коллекции!!

Альфа-ответ привел меня к обходному пути, когда вы создаете следующую структуру классов:

abstract class BaseClass extends BaseModel
{
    // ParentClass member variables and functions go here, to be shared between parent and child classes
}

class ParentClass extends BaseClass
{
    protected $table = 'your_table';

    // place no other member variables or functions in this class
}

class ChildClass extends BaseClass
{
    protected $table = 'your_table';

    // ChildClass member variables and functions go here
}

Это заставляет Laravel делать правильный поиск, когда вы вызываете ParentClass:: all() или ChildClass:: all(), поскольку их общий предок не имеет объявленной таблицы. Просто рассматривайте BaseObject как ParentObject и таким образом вам не нужно мутить концепцию наследования с данными базы данных, которые не должны быть релевантными. Я до сих пор не испытывал стресс-тестирование, поэтому не забудьте указать причину существования этого класса в вашем коде для будущих патронов для отладки.

Ответ 4

Единственная ORM в PHP, которую я знаю, которая делает одну таблицу для иерархии классов, - это Doctrine. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html. И я считаю, что он может отобразить такую ​​иерархию, которую вы планируете использовать, абстрактный суперкласс, который может получить доступ ко всем подклассам.

Чтобы интегрировать Doctrine в Laravel, я предлагаю использовать пакет laravel-doctrine, который вам нужен всю информацию на этом веб-сайте http://www.laraveldoctrine.org/.