Laravel中的模型继承

时间:2014-05-31 19:09:36

标签: laravel model eloquent

我目前正在与Laravel合作,我正在努力解决这样一个事实,即每个模型都需要从Eloquent扩展而我不确定如何实现模型层次结构(使用不同的表)

例如:

假设我有一个抽象模型Tool,然后是一个从工具扩展的模型Hammer和模型Screwdriver

现在,工具会扩展Eloquent ...但是,没有工具表,有一个Hammers表和另一个螺丝刀表,因为它们有不同的属性。

如何指定Hammer有一个表,并且当螺丝刀扩展工具时,它有一个表?我如何使用Eloquent来调用,例如,所有工具?

喜欢:

Tools::all()

这应该带来所有的锤子和螺丝刀,因为它们都是工具

使用Eloquent可以吗?

4 个答案:

答案 0 :(得分:10)

尽管我喜欢多态关系(PR)方法略好于单表继承(STI),但它仍然感觉不像真正的继承方法。从域(例如UML类图)的角度来看,它就像尝试使用组合关系而不是继承。

从数据库一致性的角度来看,多态关系本身在Laravel(恕我直言)中已经是一个非常奇怪的解决方案。由于不能有单个字段作为多个表的外键,这可能导致加入不应加入的ID。和不遵守的反向关系。

虽然我实际上并没有描述问题的解决方案,但我提出的方法与PR和STI不同。此解决方案类似于Hibernate's table-per-subclass方法。我认为Dauce's extension to Eloquent朝着同一个方向发展,除了似乎仍有一些实施问题。

从数据库的角度来看,每个子类的表也意味着超类每个直接子类包含一列。然后,您可以将外键约束放在(非null)id上,并正确使用这些关系。但是,在Laravel的魔法Eloquent类中仍然应该有一些额外的逻辑,它将请求的对象转换为正确的类型。

所以对我来说,功能上,Eloquent模型应该在PHP端正确继承,而数据库仍然可以使用外键约束。

答案 1 :(得分:8)

  

注意:您不能直接使用抽象类,必须通过子类扩展

如果您的Toolabstract)模型没有映射到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
}

直接使用HammerScrewdriver但不使用Tool模型/类,因为它是abstract类,例如:

$hammers = Hammer:all();

也许是这样的:

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

答案 2 :(得分:1)

我发现 bug (功能?),如果ParentClass和ChildClass具有相同的$ table字符串,Laravel 5会查找不正确的类。因此,如果在某些情况下调用ParentClass :: all(),它可以返回集合!!中的ChildClass实例

The Alpha's answer让我找到了一种解决方法,您可以在其中创建以下类结构:

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,这样您就不必使用不应该相关的数据库详细信息来混淆继承概念。我还没有彻底地对此进行压力测试,所以请务必在代码中注明这个类存在的原因,以便将来调试面包屑。

答案 3 :(得分:1)

我知道PHP中唯一一个每个类层次结构都有一个表的ORM是Doctrine。 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html。我相信它可以映射您计划使用的这种层次结构,一个可以访问所有子类的抽象超类。

为了将Doctrine整合到Laravel,我建议使用laravel-doctrine软件包,您需要在本网站http://www.laraveldoctrine.org/中获取所有信息。