Laravel 中的模型继承

Posted

技术标签:

【中文标题】Laravel 中的模型继承【英文标题】:Model Inheritance in Laravel 【发布时间】:2014-07-21 08:24:03 【问题描述】:

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

例如:

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

现在,Tool 将扩展 Eloquent ...但是,没有用于工具的表,有一个用于锤子的表和另一个用于螺丝刀的表,因为它们具有不同的属性。

当Hammer 和Screwdriver 都extend 工具时,我如何指定Hammer 有一个表格和Screwdriver 有一个表格?以及如何使用 Eloquent 调用,例如,所有工具?

喜欢:

Tools::all()

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

这可以使用 Eloquent 吗?

【问题讨论】:

【参考方案1】:

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

如果您的 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

直接使用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');

【讨论】:

您没有正确说出您的问题,但询问了如何使用扩展 Eloquentabstract 类。无论如何,很高兴看到您的问题已经解决。 对错误信息感到抱歉。我习惯于处理 java/hibernate 并且它更直接。反正你【参考方案2】:

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

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

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

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

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

【讨论】:

【参考方案3】:

我发现了一个 错误(功能?)如果 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,这样您就不必使用不相关的数据库详细信息来混淆继承概念。我尚未对此进行彻底的压力测试,因此请务必注意代码中存在此类的原因,以便将来调试面包屑。

【讨论】:

【参考方案4】:

我所知道的 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/ 获取所有需要的信息。

【讨论】:

Silverstripe 框架还在其 ORM 中为每个模型使用一个表。非常简单。

以上是关于Laravel 中的模型继承的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Laravel 的 Eloquent 实现单表继承?

laravel 模型观察器

Laravel 5 Model Inheritance - 获取子模式时返回父数据

Laravel:模型中的动态关系

模型中的 Laravel 5 验证

Laravel 5.3 用户模型中的 CanResetPassword