dive-be / eloquent-super
Eloquent 模型轻量级 MTI(多表继承)支持
Requires
- php: ~8.3
- illuminate/database: ^11.0
- illuminate/support: ^11.0
Requires (Dev)
- larastan/larastan: ^2.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
README
轻量级 MTI(多表继承)对 Eloquent 模型的支持。
“多表继承”到底是什么意思呢?
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)。请参阅许可文件获取更多信息。