joachim-n / mutable-typed-data
可变类型数据系统
Requires
- php: ^8.0
- symfony/expression-language: ^7
Requires (Dev)
This package is auto-updated.
Last update: 2024-09-19 08:23:37 UTC
README
可变类型数据是一个类型化和结构化数据的系统。它基于Drupal的Typed Data API,主要区别在于数据结构可以基于设置的值动态更改。
免责声明
这个工作已经完成,有测试,但还需要更多润色。现在发布是因为我从五个月前的封锁开始就一直在做这个项目,现在我已经筋疲力尽了!
结构类型
每一份数据都由一个对象表示,该对象不仅包含数据,还包含其定义。
数据对象可以是简单的,只包含一个标量值
$simple_data->value = 42;
$simple_data->value = 'cake';
$simple_data->value = TRUE;
也可以是复杂的,具有子属性,这些子属性本身可以是简单的
$complex_data->alpha = 42;
$complex_data->beta = 'cake';
$complex_data->gamma = TRUE;
或也可以是复杂的
$complex_data->child->grandchild->alpha = 42;
简单或复杂的数据都可以是多个值
$multiple_simple_data[] = 'adding new value';
$multiple_simple_data[0] = 'changing existing value';
$multiple_complex_data[]->alpha = 'adding new value';
$multiple_complex_data[0]->beta = 'changing existing value';
复杂数据是可变的,这意味着它的子属性会根据控制属性的值而更改
$animal->type = 'mammal';
$animal->gestation_period = '12 months';
$animal->type = 'reptile';
// The gestation_period property is removed; other properties may now have been
added.
访问数据
简单数据始终通过'value'属性访问
$simple_data->value = 'cake';
print $simple_data->value;
复杂数据通过子属性名称访问,可以级联直到达到结构末端的简单值
$complex_data->alpha->value = 'cake';
print $complex_data->alpha->value;
$complex_data->also_complex->value = 'cake';
print $complex_data->also_complex->alpha->value;
作为简写,简单子值可以通过属性名直接设置
$complex_data->alpha = 'cake';
多个数据可以作为数值数组使用
$multiple_simple_data[0] = 'zero';
$multiple_simple_data[1] = 'one';
$multiple_simple_data[] = 'append';
注意,增量必须保持一致
$multiple_simple_data[0] = 'zero';
$multiple_simple_data[42] = 'delta too high'; // throws Exception
无论多个数据是简单还是复杂,访问数组都会产生一个数据对象
print $multiple_simple_data[0]->value;
print $multiple_complex_data[0]->alpha->value;
迭代
所有数据对象都可以迭代。
简单数据没有效果
foreach ($simple_data as $item) {
// Nothing.
}
复杂数据迭代其子属性
foreach ($complex_data as $name => $item) {
print $item->value;
}
多个数据迭代增量项
foreach ($multiple_data as $delta => $item) {
print $item->value;
}
因为迭代简单项目没有效果,所以可以将数据项递归地迭代到数据结构中,这样做是安全的。
另外,数据对象上的items()方法允许与基数无关的迭代
foreach ($simple_data->items() as $item) {
print $item->value; // Same as $simple_data->value
}
foreach ($multiple_simple_data->items() as $item) {
print $item->value;
}
当处理可能为单个或多个的属性时,这很有用,因为它在两种情况下都给出了所有值。然而,不应递归使用,因为在迭代简单项目的值时将导致无限循环。
默认值
每个数据项都可以为其定义一个默认值。默认值可以有不同的用途
- 当用户被提示输入数据时,向用户提出一个值
- 当用户没有输入时提供值。
如果存在默认值,UI不应该强制用户输入必填值,因为MTB的验证会在数据为必填且为空时设置默认值。
默认值可以定义为
- 一个字面值,如42或'cake'
- 一个用Symfony表达式语言编写的表达式
- 一个PHP可调用项,例如类方法或匿名函数。
表达式和可调用默认值可以使用其他数据项的值,因此具有依赖关系。
以下示例说明了如何在UI中使用默认值
- UI实例化了数据属性。默认值尚未应用。
- UI尝试获取数据的值。如果默认值的依赖得到满足,则应用它。
- 用户将看到空值或已应用的默认值。
- 用户提交数据。即使属性设置为必填,UI也应允许用户留空,因为默认值可以提供值。
- 数据消费者获取值。如果它仍然为空,则应用默认值。
表达式语言和JavaScript
MTD通过自定义函数扩展了Symfony表达式语言,以获取带有地址的数据值。可以通过继承\MutableTypedData\DataItemFactory添加更多自定义函数。
使用表达式特别适合在Web UI中展示的数据,因为可以将表达式语言的小部分解析为JavaScript,从而根据用户输入的其他值动态显示默认值。参见Drupal的Module Builder示例了解如何实现这一点。
数据定义
数据通过流畅的接口进行定义
$definition = \MutableTypedData\Definition\DataDefinition::create('string')
->setLabel('Label')
->setRequired(TRUE);
复杂数据通过嵌套属性进行定义
$definition = \MutableTypedData\Definition\DataDefinition::create('complex')
->setLabel('Label')
->setProperties([
'child_property_alpha' => \MutableTypedData\Definition\DataDefinition::create('string')
->setLabel('Alpha')
->setRequired(TRUE),
'child_property_beta' => \MutableTypedData\Definition\DataDefinition::create('string')
->setLabel('Beta'),
]);
选项
选项可以用数组或对象定义,这允许选项具有描述、权重以及标签
$definition = \MutableTypedData\Definition\DataDefinition::create('string')
->setLabel('Label')
->setOptionsArray([
'green' => 'Emerald',
'red' => 'Magenta',
'grey' => 'Grey',
]);
$definition = \MutableTypedData\Definition\DataDefinition::create('string')
->setLabel('Label')
->setOptions(
\MutableTypedData\Definition\OptionDefinition::create('green', 'Emerald', 'A lovely shade of green'),
\MutableTypedData\Definition\OptionDefinition::create('red', 'Magenta', 'A deep red'),
\MutableTypedData\Definition\OptionDefinition::create('grey', 'Grey', 'Not very colourful but shows at the top', -10)
);
较高的权重“下沉”到选项列表的底部;较低的权重较轻,会“上升”。
可变数据
可变数据需要一个属性来控制变体,并为每个变体提供一个定义
$definition = \MutableTypedData\Definition\DataDefinition::create('mutable')
->setLabel('Label')
->setProperties([
'type' => DataDefinition::create('string')
->setLabel('Type')
])
->setVariants([
'alpha' => VariantDefinition::create()
->setLabel('Alpha')
->setProperties([
'alpha_one' => DataDefinition::create('string')
->setLabel('A1'),
'alpha_two' => DataDefinition::create('string')
->setLabel('A2'),
]),
'beta' => VariantDefinition::create()
->setLabel('Beta')
->setProperties([
'beta_one' => DataDefinition::create('string')
->setLabel('B1'),
'beta_two' => DataDefinition::create('string')
->setLabel('B2'),
]),
]);
变体自动定义了类型属性的选项。
如果类型属性的值与变体名称不匹配(通常是因为类型属性的多个选项值对应同一个变体),则使用setOptions()定义选项并使用setVariantMapping()。