PHP
laravel
STI
laravel5.5

laravelで単一テーブル継承の実装を試してみた話

Laravelで単一テーブル継承(STI)を使った実装する機会があり、
調べたところLaravel本体には機能がないらしく独自に実装する必要があるようなので

今回、下記の記事を参考に実装してみました。
参考にさせていただきありがとうございます。

参考記事

Laravel(Eloquent)でSingle Table Inheritance (STI) を実現する
Laravelで、単一テーブル継承(STI)を簡単に実現

環境

  • PHP 7.2
  • laravel 5.5

実装

今回はトレイトを使って実装

trait SingleTableInheritance 
{
    public function newFromBuilder($attributes = [], $connection = null)
    {
        $attributes = (array) $attributes;

        $type = $attributes['type'];
        $class = $this->getSingleTableClassMap();

        if (isset($class[$type])) {

            $model = (new $class[$type]())->newInstance([], true);
            $model->setRawAttributes((array) $attributes, true);
            $model->setConnection($connection ?: $this->getConnectionName());
            $model->fireModelEvent('retrieved', false);
            return $model;

        }

        parent::newFromBuilder($attributes, $connection);
    }

    protected function getSingleTableClassMap()
    {
        return [
];
    }
}

このトレイトをモデルクラスで利用し、getSingleTableClassMap()メソッドの戻り値にTypeに紐づくクラスを指定する

class Member extends Model
{
    use SingleTableInheritance;

    protected function getSingleTableClassMap()
    {
        return [
            'GoldMember' => GoldMember::class,
            'SilverMember' => SilverMember::class,
            'BronzeMember' => BronzeMember::class
        ];
    }
}

$member = Member::find(1); // 3つどれか

これでID指定で会員を取得した際にどれかのクラスでオブジェクトが作られる。

あとは、「GoldMember」の会員のみ取得する機会があると思うのでGlobalScopeの機能を使う。
GlobalScopeを使うことで常に条件にtypeを含めるようにする。

class GoldMember extends Member
{
    protected static function boot()
    {
        parent::boot();

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

$goldMembers = GoldMember::all();
// select * from member where type = 'GoldMember'; ← 実行SQL

SilverMember、BronzeMemberのbootメソッドにも書けば同じように実行できる。

この仕組みを使えば
ECサイトなどで会員毎に振る舞いをサブクラス側で用意し、
Memberクラスを使うだけでそれぞれの振る舞いを実行できる(はず)