Laravel 中的模型继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23973850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 09:35:16  来源:igfitidea点击:

Model Inheritance in Laravel

laravelmodeleloquent

提问by Gabriel Matusevich

i'm currently working with Laravel and I'm struggling with the fact that every model needs to extend from Eloquent and I'm not sure how to implement a model Hierarchy(with different tables)

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

For Example:

例如:

Let's say I have an abstract model Tool, and then a Model Hammerand a Model Screwdriverthat extend from Tool.

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

Now, Tool would extend Eloquent ... BUT, there is NO Table for Tools, there is a table for Hammers and another Table for Screwdrivers, because they have different attributes.

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

How do I specify that Hammer has a table and Screwdriver has a table when they both extendTool? And how can I use Eloquent to call, For Example, All Tools?

当它们都扩展Tool时,如何指定 Hammer 有一张桌子,Screwdriver 有一张桌子?以及如何使用 Eloquent 调用例如所有工具?

Like:

喜欢:

Tools::all()

This Should bring all Hammers and Screwdrivers because they are all Tools

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

Is this possible using Eloquent?

这可以使用 Eloquent 吗?

回答by Joost

Even though I like the Polymorphic Relations (PR) approach slightly better than the Single Table Inheritance (STI), it still does not feel anything like a true inheritance approach. From the domain (e.g. a UML Class Diagram) point of view, it is like trying to use a composition relationship instead of inheritance.

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

From a database consistency point of view the polymorphic relation by itself is already a very weird solution in Laravel (IMHO). As there can be no single field that is a foreign key to multiple tables, this can lead to joining ID's that should not be joined. And inverse relationships that are not obeyed.

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

Although I'm not actually describing a solution to the problem, I'd propose a different approach than PR and STI. This solution would be similar to Hibernate's table-per-subclassapproach. I think that Dauce's extension to Eloquentis going in the same direction, except there seem to be a few implementation issues still.

虽然我实际上并不是在描述问题的解决方案,但我会提出一种与 PR 和 STI 不同的方法。这个解决方案类似于Hibernate 的 table-per-subclass方法。我认为Dauce 对 Eloquent 的扩展正朝着相同的方向发展,除了似乎仍然存在一些实现问题。

From a database point of view, a table per subclass would also mean that the super-class contains one column per direct subclass. Then you can put foreign key constraints on the (non-null) id's and properly use the relationships. However, there should still be some extra logic in Laravel's magic Eloquent class that turns the requested object into the right type.

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

So for me, functionally, the Eloquent models should properly inherit on the PHP side, while the database can still use the foreign key constraints.

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

回答by The Alpha

Note: You can't use an abstract class directly, it has to be extended by a child class

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

If your Tool(abstract) model doesn't have any tablemapped to it then you don't need to use Tool::alland you can't directly use/instantiate an abstractmodel but you may use an that abstractmodel as a base class like this:

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

Use Hammerand Screwdriverdirectly but never the Toolmodel/class because it's an abstractclass, for example:

直接使用Hammerand Screwdriver,但不要使用Tool模型/类,因为它是一个abstract类,例如:

$hammers = Hammer:all();

Or maybe something like this:

或者可能是这样的:

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

回答by Zack Morris

I found a bug(feature?) where Laravel 5 looks up the incorrect class if ParentClass and ChildClass have the same $table string. So if you call ParentClass::all() in certain situations it can return ChildClass instances in the collection!!

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

The Alpha's answerled me to a workaround where you create the following class structure:

Alpha 的回答让我找到了一种解决方法,您可以在其中创建以下类结构:

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
}

This seems to force Laravel to do the correct lookup when you call ParentClass::all() or ChildClass::all() since their common ancestor doesn't have a table declared. Just treat BaseObject as ParentObject and that way you don't have to muddy your inheritance concept with database details that shouldn't be relevant. I have not stress-tested this thoroughly yet, so be sure to note the reason for this class's existence in your code for future debugging breadcrumbs.

这似乎迫使 Laravel 在您调用 ParentClass::all() 或 ChildClass::all() 时进行正确的查找,因为它们的共同祖先没有声明表。只需将 BaseObject 视为 ParentObject,这样您就不必用不相关的数据库详细信息混淆您的继承概念。我还没有对此进行彻底的压力测试,所以一定要注意这个类在你的代码中存在的原因,以便将来调试面包屑。

回答by Leandro Jacques

The only ORM in PHP that I know that does a one table per class hierarchy is Doctrine. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html. And I believe that it can map this kind of hierarchy you're planning to use, an abstract superclass that can access all subclasses.

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

To integrate Doctrine to Laravel, I suggest to use laravel-doctrine package which you have all needed info in this website http://www.laraveldoctrine.org/.

要将 Doctrine 集成到 Laravel,我建议使用 laravel-doctrine 包,您在本网站http://www.laraveldoctrine.org/ 中拥有所有需要的信息。