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

时间:2014-11-01 18:22:20

标签: mysql inheritance laravel eloquent subclass

目前我有一个名为Post的模型类。

class Post extends Eloquent {

    protected $table = 'posts';
    protected $fillable = array('user_id', 'title', 'description', 'views');

    /*
     * Relationships
     */

    public function user()
    {
        return $this->belongsTo('User');
    }

    public function tags()
    {
        return $this->belongsToMany('Tag', 'post_tags');
    }

    public function reactions()
    {
        return $this->hasMany('Reaction');
    }

    public function votes()
    {
        return $this->hasMany('PostVote');
    }

    //Scopes and functions...
}

我想将帖子分成两种不同的类型; articlesquestions。我认为最好的方法是继承,因此ArticleQuestion会扩展Post。这样做的最佳方式是什么,从哪里开始?

2 个答案:

答案 0 :(得分:52)

在深入研究多表继承之前,我想失去一些关于单表继承的话。在使用db模型进行继承时,单表继承是更简单的方法 您有多个绑定到同一个表的模型和一个type列,以区分不同的模型类。但是,您通常希望实现继承的原因是因为模型具有共享属性,但也有一些对模型而言是唯一的 使用单表继承时,您的表看起来与某些点类似:

id   shared_column   question_column   article_column   question_column2   article_column2 etc...
1    Lorem           62                NULL             test               NULL
2    Ipsum           NULL              x                NULL               true

您最终会拥有大量NULL值,因为某些类型的模型不需要列。并且有很多记录,这会影响数据库的大小。

然而,在某些情况下,它可能仍然是最好的解决方案。这是一篇精心编写的Tutorial,展示了如何以非常优雅的方式在Laravel中实现它。

多表继承

现在让我们看看多表继承。使用这种方法,你可以将你的单个表分成多个表(我想这个名字已经提供了那种;))我们将使用一种名为Polymorphism的技术

上面示例中的模式如下所示:

posts table:

id   shared_column  postable_id  postable_type
1    Lorem          1            Question
2    Ipsum          1            Article


questions table:

id   question_column   question_column2
1    62                test


articles table:

id   article_column   article_column2
1    x                true

如果你问我那么干净......

这里有趣的列是postable_idpostable_type。该类型告诉我们在哪个表中我们将找到模型的“rest”,id指定属于它的记录的主键。请注意,列名称可以是您想要的任何名称,但通常称之为“ - able”

现在让我们来看看Eloquent模型。

发表

class Post extends Eloquent {
    // all your existing code

    public function postable(){
        return $this->morphTo();
    }
}

问题 / 文章 / 其他所有可用类型

class Question extends Post {
    public function post(){
        return $this->morphOne('Post', 'postable');
    }
}

请注意,您实际上不必从Post扩展,但如果您可能也有想要使用的方法,则可以。无论如何,多态关系将在有或没有它的情况下起作用。

好的,这是基本设置。以下是使用新模型的方法:

检索所有帖子

$posts = Post::all();

检索所有问题

$questions = Question::all();

从帖子中获取问题列

$post = Post::find(1);
$question_column2 = $post->postable->question_column2;

从问题中获取帖子属性

$question = Question::find(1);
$shared_column = $question->post->shared_column;

检查帖子的类型

$post = Post::find(1);
echo 'type: '.get_class($post->postable);
if($post->postable instanceof Question){
    // Looks like we got a question here
}

创建新问题

现在忍受我,创建一个模型有点复杂。如果您必须在应用程序的多个位置执行此操作,我建议您为其编写可重用的功能。

// create a record in the questions and posts table

$question = new Question();
$question->question_column = 'test';
$question->save();

$post = new Post();
$post->shared_column = 'New Question Post';
$post->save();

// link them together

$question->post()->save($post);

正如您所看到的,一个干净的数据库附带它的价格。处理模型会有点复杂。但是,您可以将所有这些额外的逻辑(例如,创建模型所需的逻辑)放入模型类中的函数中,而不必过多担心。

此外,对于具有laravel的多表继承,还有一个很好的Tutorial。也许它有帮助;)

答案 1 :(得分:3)

来自Laravel 5.2的

Global Scope可用:

class Article extends Post
{
    protected $table = 'posts';

    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('article', function (Builder $builder) {
            $builder->where('type', 'article');
        });

        static::creating(function ($article) {
            $article->type = 'article' 
        }); 
    }
}

where type = 'article'已添加到Article的所有查询中,就像SoftDeletes一样。

>>> App\Article::where('id', 1)->toSql()
=> "select * from `posts` where `id` = ? and `type` = ?"

实际上,laravel使用此功能提供SoftDeletes特征。