mannikj/laravel-sti

适用于您 eloquent 模型的单表继承特性

v5.0.2 2023-02-07 15:22 UTC

README

Latest Version on Packagist Build Status Total Downloads

此包提供了一种特性,使您的 eloquent 模型能够实现单表继承。如果配置正确,查询将自动返回正确的模型子类实例,根据类型列。

安装

您可以通过 composer 安装此包

composer require mannikj/laravel-sti

基本用法

迁移

您要应用单表继承的表必须包含一个类型列。

包的服务提供程序向 Blueprint 类添加了一个宏,用于创建类型列。

Schema::table('table', function (Blueprint $table) {
    $table->sti()->nullable();
});

使用特性

您需要将 SingleTableInheritance 特性添加到您的根模型类中。子模型需要扩展根类。

use MannikJ\Laravel\SingleTableInheritance\Traits\SingleTableInheritance;

class Root extends Model {
    use SingleTableInheritance;
}

class Sub1 extends Root {}

class Sub2 extends Root {}

对于默认用法,无需其他配置。特性将使用子类的类名作为类型,相应地范围查询,并自动返回正确的子类实例。

嵌套

如果您有多个子类层级,并且希望自动范围包括所有子类型,您需要通过设置 stiSubClasses 数组属性来为每个模型定义直接子类。

use MannikJ\Laravel\SingleTableInheritance\Traits\SingleTableInheritance;

class Root extends Model {
    use SingleTableInheritance;

    protected $stiSubClasses = [
        Sub1::class, Sub2::class
    ]
}

class Sub1 extends Root {
    protected $stiSubClasses = [
        Sub3::class
    ]
}

class Sub2 extends Root {}

class Sub3 extends Sub1 {}

高级用法

没有完全限定类名的类型

默认情况下,特性假定有一个类型列存储子类的完全限定名称。但是,如果您想使用不直接引用类的其他字符串作为类型标识符,可以通过覆盖特性的两个函数来实现。

    public static function resolveTypeViaClass()
    {
        $type = (new \ReflectionClass(static::class))->getShortName();
        $type = str_replace('Component', '', $type);
        $type = strtolower($type);

        return static::isSubclass() 
            ? $type 
            : null;
    }


    public function resolveModelClassViaAttributes($attributes = [])
    {
        $type = $this->resolveTypeViaAttributes($attributes);

        // Map class to type
        $mapping = [
            'motif' => MotifComponent::class,
            'text' => TextComponent::class,
        ];
        
        return $type 
            ? data_get($mapping, $type) 
            : static::class;
    }

根据相关模型解决类型

您还可以进一步调整行为,使类型根据相关模型来确定。

class Animal extends Model
{
    use SingleTableInheritance;

    protected $fillable = ['name'];

    public function resolveTypeViaAttributes($attributes = [])
    {
        if ($category = Category::find(Arr::get($attributes, 'category_id'))) {
            return $category->config_class;
        };
    }

    public function applyTypeCharacteristics($type)
    {
        $this->category_id = Category::where('config_class', $type)->first()?->id;
    }

    public function scopeSti(Builder $builder)
    {
        $builder->whereHas('category', function ($query) use ($builder) {
            $query->where('categories.config_class', static::class);
        });
    }

    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id')->withDefault([
            'config_class' => static::class
        ]);
    }
}

测试

composer test

变更日志

有关最近更改的更多信息,请参阅CHANGELOG

贡献

有关详细信息,请参阅CONTRIBUTING

安全

如果您发现任何与安全相关的问题,请通过mannikj@web.de发送电子邮件,而不是使用问题跟踪器。

鸣谢

许可

MIT 许可证 (MIT)。有关更多信息,请参阅许可文件

Laravel 包模板

此包是用Laravel 包模板生成的。