dive-be/eloquent-super

Eloquent 模型轻量级 MTI(多表继承)支持

1.3.0 2024-03-14 12:30 UTC

This package is auto-updated.

Last update: 2024-09-22 12:35:06 UTC


README

轻量级 MTI(多表继承)对 Eloquent 模型的支持。

Latest Version on Packagist Software License Total Downloads

“多表继承”到底是什么意思呢?

MTI 允许为每个共享“超类”的“子类”拥有独立的数据库表。

“我难道不能在表中定义一个类型列然后解决问题吗?”

这取决于情况。如果您只想为每个子类型(例如 user - admin)添加特定的行为,那么 单表继承 绝对是更好的选择。

然而,如果子类型具有非常不同的数据字段,那么 MTI 是更好的工具。在这种情况下使用 STI 会导致相关的表拥有很多 NULL 列。

这个包解决了什么问题?

简答

实际上,它解决的是“零问题”。“为什么会有这个包?”你可能会问。好吧,继续看下去。

长答

你看,Eloquent 已经给了我们定义多态关系的能力。您只需要开始利用 Eloquent 的 MTI 功能,就需要一个 MorphOne 关系。这个包在现有功能上增加了一个很好的 DX 层,因此它更容易与这些紧密耦合的关系一起工作。

因此,这个包的“核心”是**委托调用**到定义的 super 关系(以及其他一些事情)。在面向对象的意义上,并没有真正的“父”类。这是一个有意识的决策,不希望在模型上添加太多的魔法。

安装

composer require dive-be/eloquent-super

用法

超级/父类 👱🏻‍♂️

迁移

超级模型应该定义一个符合 Laravel 命名约定的 morphs 关系:模型的单数名称加上 able

Schema::create('addresses', static function (Blueprint $table) {
    $table->id();
    $table->foreignId('country_id')->constrained();
    $table->morphs('addressable'); // ==> mandatory
    
    // ... other columns
});

类定义

超级类 必须 定义一个 fillable 数组,以便确定哪些属性属于哪个数据库表。没有它,就无法区分超级的列和子类的列。

class Address extends Model
{
    protected $fillable = ['city', 'country_id', 'street', 'postal_code'];

    public function country(): BelongsTo
    {
        return $this->belongsTo(Country::class);
    }
}

子/子类 👶🏼

子类不一定需要定义一个 fillable 数组。将 $guarded 设置为空数组也是完全可以的。

类定义

class ShippingAddress extends Model
{
    use \Dive\EloquentSuper\InheritsFromSuper;

    protected $fillable = ['email', 'phone', 'contact_person', 'is_expedited', 'courier'];
    
    protected function getSuperClass(): string
    {
        return Address::class;
    }
}
class InvoiceAddress extends Model
{
    use \Dive\EloquentSuper\InheritsFromSuper;

    protected $fillable = ['company_id', 'email', 'fax', 'phone', 'language'];
    
    protected function getSuperClass(): string
    {
        return Address::class;
    }
}

功能 💪

保存子模型时的数据分区

$address = ShippingAddress::create($request->validated());

$address->getAttributes(); // 'email', 'phone', 'contact_person', 'is_expedited', 'courier'
$address->super->getAttributes(); // 'city', 'country_id', 'street', 'postal_code'

从超级模型检索属性/关系

$address->city; // Ghent
$address->super->city; // Ghent

$address->country; // App\Models\Country { #2981 }
$address->super->country; // App\Models\Country { #2981 }

删除超级和子模型

$address->delete(); // Database transaction in the background

注意:如果超级和子模型都使用了“SoftDeletes”特性,则只有子模型会被软删除

关于始终预加载“超级”关系的说明 📣

子模型不存在于没有来自超级模型的补充数据的情境中。通过拥有两个表,我们能够实现一个规范化的数据库,但在代码中,只有当它们作为一个整体存在时才有意义。

测试

composer test

变更日志

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

贡献

有关详细信息,请参阅 CONTRIBUTING

安全性

如果您发现任何安全问题,请通过电子邮件 oss@dive.be 而不是使用问题跟踪器。

鸣谢

许可证

麻省理工学院许可证(MIT)。请参阅许可文件获取更多信息。