web-dev-qa-db-ja.com

LaravelのEloquentを使用して単一テーブルの継承を実装するにはどうすればよいですか?

現在、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...
}

投稿を2つのタイプに分けたいと思います。 articlesおよびquestions。これを行う最良の方法は継承によるものだと思ったので、ArticleQuestionPostを拡張します。これを行う最善の方法は何ですか?

26
JasonK

multi table継承について詳しく調べる前に、single table継承について少し説明しておきます。単一テーブルの継承は、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_idおよびpostable_type。タイプにより、モデルの「残りの部分」が見つかるテーブルがわかります。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 もあります。多分それは役に立ちます;)

53
lukasgeiter

から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特性を提供します。

4
Kazuya Gosho