当前位置: 首页 > 文档资料 > FuelPHP 中文文档 >

EAV 容器 - Orm 套件

优质
小牛编辑
131浏览
2023-12-01

「实体-属性-值模型(EAV)是一种实体资料模型,描述该实体属性(特性、参数)的数量可能十分巨大, 但实际上会被应用在所给实体的数量相对适中。 在数学上,这个模型被称为稀疏矩阵。EAV 也被称为物件-属性-值模型, 直式的资料库模型以及开放的架构。」

来源:Wikipedia (English)

简介

大多数人已经在第一段迷失,所以让我们试着用一个範例来解释。

当你有一个实体(纪录在 ORM 中的术语)而它有关联的属性做为它的子代时,你通常会使用一个 EAV 容器。 但是,对每一条纪录来说,这些属性可以是不同的。 这使得它不可能在实体的资料表中定义这些属性为行,因为会有太多, 他们大多不会有资料,而且你完全不能处理动态属性 (因为行需要预先被定义)。

要解决这个「关联」风格的问题,你会建立一张子表,并使用一对多关係关联到「实体」的资料表, 其中每个属性会成为子表中的一笔纪录。 然而,这种作法的缺点是,为了能够取得一个指定属性值,你将不得不循环所有关联纪录, 比较属性行与你寻找的值,如果找到一个符合, 取得该值行的内容。

ORM EAV 容器使用这个相同的实现,但允许你合併实体的属性, 如此该属性成为实体纪录的特性, 从而模拟一个 EAV 实现所需的可变行数。

实现

要在一个模型启用 EAV 容器,该模型需要一张有属性及值行的子表, 它需要被定义为一对多或多对多关联。

让我们建立一个医院病人的範例,该医院保留病人的纪录统计。 这取决于病人的病情,他们不能被预先预测。

class Model_Patient extends \Orm\Model
{
	// 此模型的特性列表,在这个例子中简化
	protected static $_properties = array(
		'id',					// 主键
	);

	// 设置 statistics 关联的通常方法
	protected static $_has_many = array(
		'statistics' => array(
'key_from' => 'id',			// 在此模型中的键
'model_to' => 'Model_Statistic',	// 关联模型
'key_to' => 'patient_id',		// 在关联模型中的键
'cascade_save' => true,		// 在储存时更新关联资料表
'cascade_delete' => true,		// 在删除父层时删除关联资料
		)
	);

	// 像这样定义 EAV 容器
	protected static $_eav = array(
		'statistics' => array(			// 我们使用 statistics 关联来储存 EAV 资料
'model_to' => 'Model_Statistic',	// 关联模型
'attribute' => 'key',		// 在关联资料表中包含属性的键行
'value' => 'value',			// 在关联资料表中包含值的值行
		)
	);
}

class Model_Statistic extend \Orm\Model
{
	// 此模型的特性列表
	protected static $_properties = array(
		'id',					// 主键
		'patient_id',				// 外键
		'key',					// 属性行
		'value',				// 值行
	);

	// 设置病人关联的通常方法
	protected static $_belongs_to = array(
	    'patient' => array(
	        'key_from' => 'patient_id',
	        'model_to' => 'Model_Patient',
	        'key_to' => 'id',
	        'cascade_save' => true,
	        'cascade_delete' => true,
	    )
	);
}

你可以定义多个 EAV 容器,连结到不同的关联资料表。如果你这样做, 请求一个属性会依他们定义的顺序来搜寻整个容器,直到找到一个符合。 正如这仅仅是一个模型属性的额外定位,模型中处理未定义的特性仍保持不变: 它会抛出一个例外。

範例

使用上面的配置,假设我们有以下的资料集:

// SELECT * FROM patient
+----+-----------+
| id | name      |
+----+-----------+
|  1 | MisterIll |
|  2 | MissIll   |
+----+-----------+

// SELECT * FROM statistics
+----+------------+---------------+----------------+
| id | patient_id | key           | value          |
+----+------------+---------------+ ---------------+
|  1 |          1 | Temperature   |           38.4 |
|  2 |          1 | Coughing      |            yes |
|  3 |          1 | Headache      |             no |
|  4 |          2 | Temperature   |           37.9 |
|  5 |          2 | Heartbeat     |             98 |
+----+------------+---------------+----------------+

所给的这些资料,你可以像这样存取:

// 建立一些物件来协作
$mr = Patient::find(1);
$ms = Patient::find(2);

// 现在你可以直接取得属性
echo $mr->Temperature;		// '38.4'
echo $ms->Temperature;		// '37.9'
echo $mr->Headache;		// 'yes'
echo $ms->Headache;		// 抛出一个特性找不到的例外

// 或直接设定他们
$mr->Temperature = '36.9';	// 我们的病人似乎有改善
$mr->save();			// 更新病人纪录,并且更新 EAV 纪录

// 你仍可以用古老的方法存取资料
foreach ($mr->statistics as $statistic)
{
	// 在这里对每个统计值做点什幺……
}

请注意,当你的模型有一个已定义的 EAV 容器,你可以不用再为模型使用自订资料属性, 因为每个新属性会视为一个新的 EAV 键。