mswap / 单表继承
单表继承特性
Requires
- php: >= 5.5.9
- illuminate/database: 5.2.*
- illuminate/support: 5.2.*
Requires (Dev)
- mockery/mockery: 0.9.*
- orchestra/testbench: 3.2.*
- phpunit/phpunit: ~4.8|~5.2
README
此分支包含使代码与MongoDB兼容所需的修改。(此版本已测试,仅适用于mongodb)从 https://github.com/Nanigans/single-table-inheritance 分支而来
单表继承
单表继承是用于Laravel 5.2+ Eloquent模型的特性,允许多个模型存储在同一个数据库表中。我们支持一些关键特性
- 作为特性实现,以便与其他特性良好地协作,例如Laravel的
SoftDeletingTrait或优秀的Validating,而不需要复杂的Eloquent模型子类。 - 允许任意类层次结构,而不仅仅是两级的父子关系。
- 可自定义用于存储模型类型的数据库列名。
- 可自定义存储在数据库中模型类型值的字符串。(而不是强制使用完全限定的模型类名。)
- 允许数据库行不映射到已知的模型类型。它们永远不会在查询中返回。
安装
简单地将包添加到您的composer.json文件中,并运行composer update。
"mswap/single-table-inheritance": "0.1.*"
或者进入包含composer.json文件的您的项目目录,并输入
composer require "mswap/single-table-inheritance:0.1.*"
概述
开始使用单表继承特性非常简单。在模型中添加约束并添加一些属性。以下是一个完整的示例,展示了具有两个子类Truck和Car的Vehicle超级类:
use mswap\SingleTableInheritance\SingleTableInheritanceTrait; class Vehicle extends Model { use SingleTableInheritanceTrait; protected $table = "vehicles"; protected static $singleTableTypeField = 'type'; protected static $singleTableSubclasses = [Car::class, Truck::class]; } class Car extends Vehicle { protected static $singleTableType = 'car'; } class Truck extends Vehicle { protected static $singleTableType = 'truck'; }
在您的类中需要定义四个必需的属性
定义数据库表
在根模型中设置protected属性$table以定义用于存储所有类的数据库表。
注意:即使您正在使用根类的默认值(即Vehicle类的'vehicles'表),这也需要这样做,以便子类继承相同的设置,而不是默认到它们自己的表名。
定义存储类类型的数据库列
在根模型中设置protected static属性$singleTableTypeField以定义用于存储每个类类型的数据库列。
定义子类
在根模型和每个分支模型中定义protected static属性$singleTableSubclasses以定义哪些子类是类层次结构的一部分。
定义类类型值
在每个具体类中设置protected static属性$singleTableType以定义存储在$singleTableTypeField数据库列中的此类的字符串值。
多层类层次结构
类层次结构中有很多层级并不罕见。通过在每一级声明子类,可以轻松定义该结构。例如,假设您有一个Vehicle超级类,它有两个子类Bike和MotorVehicle。其中MotorVehicle又有两个子类Car和Truck。您将定义类如下所示:
use mswap\SingleTableInheritance\SingleTableInheritanceTrait; class Vehicle extends Model { use SingleTableInheritanceTrait; protected $table = "vehicles"; protected static $singleTableTypeField = 'type'; protected static $singleTableSubclasses = [MotorVehicle::class, Bike::class]; } class MotorVehicle extends Vehicle { protected static $singleTableSubclasses = [Car::class, Truck::class]; } class Car extends MotorVehicle { protected static $singleTableType = 'car'; } class Truck extends MotorVehicle { protected static $singleTableType = 'truck'; } class Bike extends Vehicle { protected static $singleTableType = 'bike'; }
定义要持久化的属性
Eloquent 允许您获取和设置属性非常宽松。没有机制来声明模型支持的属性集。如果误用属性,尝试对不存在列进行插入或更新时,通常会导致 SQL 错误。默认情况下,SingleTableInheritanceTrait 以相同的方式运行。然而,当在单个表中存储类层次结构时,通常存在不适用于层次结构中所有类的数据库列。Eloquent 将值存储在这些列中,这使得编写错误更加容易。在那里,SingleTableInheritanceTrait 允许您定义哪些属性被持久化。持久化的属性集也继承自父类。
class Vehicle extends Model { protected static $persisted = ['color'] } class MotorVehicle extends Vehicle { protected static $persisted = ['fuel'] }
在上面的示例中,类 Vehicle 将持久化属性 color,而类 MotorVehicle 将持久化 color 和 fuel。
自动持久化属性
为了方便起见,模型主键和任何日期都会自动添加到持久化属性列表中。
BelongsTo 关系
如果您正在限制持久化属性,并且您的模型有 BelongsTo 关系,则必须包含 BelongsTo 关系的 外键列。例如
class Vehicle extends Model { protected static $persisted = ['color', 'owner_id']; public function owner() { return $this->belongsTo('User', 'owner_id'); } }
遗憾的是,没有有效的方法来自动检测 BelongsTo 外键。
抛出无效属性异常
默认情况下,SingleTableInheritanceTrait 将静默地处理无效属性。在保存模型时忽略未持久化的属性,在从构建器查询中填充模型时忽略未持久化的列。然而,您可以通过将 $throwInvalidAttributeExceptions 属性设置为 true,来强制在上述任何情况下遇到无效属性时抛出异常。
/** * Whether the model should throw an InvalidAttributesException if non-persisted * attributes are encountered when saving or hydrating a model. * If not set, it will default to false. * * @var boolean */ protected static $throwInvalidAttributeExceptions = true;
灵感
我们选择了一种非常特殊的实现来支持单表继承。然而,其他人已经编写了代码和文章,围绕一种通用方法,这种方法已被证明具有影响力。
首先,Mark Smith 有一个出色的文章 Single Table Inheritance in Laravel 4,其中之一介绍了返回正确类型对象的查询的重要性。其次,Jacopo Beschi 编写并扩展了 Eloquent 的 Model,Laravel-Single-Table-Inheritance,介绍了能够定义每个模型持久化哪些属性的重要性。
特性(Traits)的使用受到 Eloquent 的 SoftDeletingTrait 和出色的 Validating Trait 的很大影响。
MongoDB 修改
此版本包含修改,以使此代码与 MongoDB 一起工作。(此版本已测试且仅适用于 mongodb)