toxaw / array-builder
Requires
- php: >=7.1
This package is auto-updated.
Last update: 2024-09-23 21:05:23 UTC
README
数组嵌套构造器。
使用手册
解决的问题
我们需要形成响应,用于计划的结构如下:[部门] 中有 [用户] 拥有 [月计划],其中包含 [休假] 和 [休息日] 参数。每个 [计划日] 有 [分配的任务] 和 [任务时间标记]。在 [分配的任务] 中存在对应 [任务预算] 的参数。
一个正常的例子是将查询构建到数据库中,使用 5-7 个连接到其他实体。是的,这已经不错了,但很痛苦。
缺点如下
- 数据量很大,连接只会使工作变得更糟;
- 编写疯狂和不理解的 foreach;
- 我们如何将不存在的实体的结构添加到结果中([月份的每一天] - 虚拟实体)?
为了解决多连接的问题,我们将对每个实体的调用分解为每个请求,其中传递了其他实体的结果,即。
- 选择部门;
- 按部门选择用户;
- 对整个月的天数进行 foreach;
- 按月份边界选择休假;
- 按月份边界选择休息日;
- 按用户和月份边界选择任务;
- 按用户和月份边界选择任务时间标记。
这样,多连接就被分解了。
接下来,我们使用 foreach 和各种条件和难以理解的代码编写单体,很容易迷路并损坏所有内容。
不幸的是,此阶段被排除在链之外,并开始使用 [嵌套构造器],示例(代码尚未全部完成)
// Набор подготовленных сущностей
$department = new Department();
$user = new User(null, $department);
$absence = new Absence(null, $user, $timeRange);
$holiday = new PlanHoliday(null, $timeRange);
// Карта response
$map = new Element('deps');
$map->addChild('users')->notEmpty()->addChild('planTimeMonth')->notEmpty()->inMerged();
$map->getChild('users')->addChild('plan')->notEmpty()->addChild('holiday')->inMerged();
$map->getChild('users')->getChild('plan')->addChild('absence')->inMerged();
// Просмотр карты визуально
//VisualizerMap::visuald($map);
// Навешивание сущностей и привязок + щепотка магий
$compiler = new Compiler($map);
// Заполняем отделы
$compiler->element('deps')->fill($department->getByIds());
// Заполняем юзеров
$compiler->element('users')->fill($user->getByIds())->addUnsetField('time')
->addNode()
->setForeign('departmentId')->unsetForeign()
->setParent('deps')->setPrimary('departmentId')
->condition()->primaryInForeign();
// Заполняем время месячный плана
$compiler->element('planTimeMonth')->fill($planYear->getByIds())
->addNode()
->condition()->all();
// Заполняем месячный план
$compiler->element('plan')->fill($timeRange->getRange())->addUnsetField('dateTime')
->addCallable(static function (&$element, $parent) {
$element['time'] = $parent['merged']['users']['time']*60;
return true;
});
// Заполняем выходные дни
$compiler->element('holiday')->fill($holiday->getByIds())
->addNode()
->setForeign('dateStart')
->setParent('plan')->setPrimary('dateTime')
->condition()->callable(static function ($dateStart, $dateTime) {
if ($dateStart->getTimestamp() <= $dateTime->getTimestamp()) {
return true;
}
})
->tie()
->addNode()
->setForeign('dateFinish')
->setParent('plan')->setPrimary('dateTime')
->condition()->callable(static function ($dateFinish, $dateTime) {
if ($dateFinish->getTimestamp() >= $dateTime->getTimestamp()) {
return true;
}
})
->tie()
->addCallable(static function (&$element) {
$element = [];
$element['isDayOff'] = true;
return true;
});
// Заполняем отпуска
$compiler->element('absence')->fill($absence->getByIds())
->addNode()
->setForeign('userId')
->setParent('users')->setPrimary('userId')
->tie()
->addNode()
->setForeign('dateStart')
->setParent('plan')->setPrimary('dateTime')
->condition()->callable(static function ($dateStart, $dateTime) {
if ($dateStart <= $dateTime) {
return true;
}
})
->tie()
->addNode()
->setForeign('dateFinish')
->setParent('plan')->setPrimary('dateTime')
->condition()->callable(static function ($dateFinish, $dateTime) {
if ($dateFinish >= $dateTime) {
return true;
}
})
->tie()
->addCallable(static function (&$element) {
$element = [];
$element['isVacation'] = true;
return true;
});
// Смотрим результат
echo '<pre>';
die(print_r($compiler->compile()));
echo '</pre>';
// А тут его отдаем ответом
return response()->json(
$compiler->compile(false)
);
在这个阶段,如何构建绑定已经很清楚。
如何使用构造器
地图构建器
Element 类
初始化对象时,接受根别名作为参数。
示例:$map = new Element([root alias name]);
方法
- addChild([alias name]) - 添加嵌套对象,在参数中包含别名,返回新的 Element;
- getChild([alias name]) - 返回嵌套对象,在参数中包含别名,返回 Element;
- getParent() - 返回嵌套对象的父对象,没有参数,返回 Element;
- one() - 指示当前嵌套对象不是集合,没有参数,返回当前 Element;
- notEmpty() - 指示当前嵌套对象不能为空,没有参数,返回当前 Element;
- inMerged() - 指示当前嵌套对象将被添加到父嵌套对象(合并(merge))中,没有参数,返回 Merged 对象,默认将合并第一个元素到集合中;
- setGroupField() - 指示嵌套对象的字段将被分组,以数组的形式,没有参数,不返回任何内容;
- setGroupFieldIfMore() - 指示嵌套对象的字段将被分组,以数组的形式,如果元素数量大于一个,没有参数,不返回任何内容。
构建地图规则
- 嵌套对象别名名称不应与父对象的名称相同;
- 同一父对象的子代中,嵌套对象别名名称必须是唯一的;
- 已设置 inMerged() 选项的嵌套对象不能有子代。
地图可视化器
VisualizerMap 类
VisualizerMap 有静态方法
- visual([map object]) - 可视化地图的构建,在参数中传递根 Element 对象;
- visuald([map object]) - visual() + die().
编译器
Compiler 类
初始化对象时,接受地图对象(Element)作为参数。
示例:$compiler = new Compiler($map);
编译器由容器组成(Container 类的对象,通过 element([alias name]) 方法初始化)。
容器内部包含数组(通过 fill([array]) 方法填充)。
容器还包含一组绑定(Node 类的对象,通过 addNode() 方法初始化)和/或包含自定义条件和操作,这些通过 addCallable([callable function]) 方法初始化。
绑定包含外键,其父元素的内键和条件类型(Condition类对象,通过condition()方法初始化)。
Compiler方法
- element([别名名称]) - 初始化容器(Container对象),该容器绑定到地图的嵌套对象,接受嵌套对象的别名名称,返回创建的Container对象;
- fill([数组]) - 向容器填充数组,接受数组(如果Element中设置了one(),则返回单个数组,否则返回一组相同类型的数组),返回Container对象;
- setName([数组键名]) - 在父数组中设置要嵌套的数组(数组)的键,接受数组(数组)的键名,返回Container对象;
- addUnsetField([数组键名]) - 设置在编译后应消失的数组键,接受数组键名,返回Container对象;
- setSaveKeys() - 设置选项,表示在从数组集合中提取时保存键值,无参数,返回Container对象;
- addCallable([可调用函数]) - 设置回调函数,函数参数中首先传入当前元素的引用,其次是一个包含两个数组的数组[merged] - 以键形式表示的父元素集合和[modified] - 与merged类似,但modified中传入的是父元素的引用,而merged中传入的是基于$element(更改后的父元素)和$elementOrigin(原始父元素)创建的父元素。回调函数应返回true或false。返回Container对象;
- addNode() - 根据其父元素初始化嵌套数组的绑定,无参数,返回创建的Node对象;
- setForeign([外键名]) - 设置通过哪个元素键进行绑定,接受键名,返回Node对象;
- unsetForeign() - 与addUnsetField()类似,但使用的是已设置的外键元素,无参数,返回Node对象;
- setParent([父别名名称]) - 设置要绑定的父嵌套对象,接受父嵌套对象的名称,返回Node对象;
- setPrimary([父主键名]) - 设置在父元素(通过setParent()确定)中的键,通过该键进行绑定,接受键名,返回Node对象;
- unsetPrimary() - 与unsetForeign()类似,但针对父元素,无参数,返回Node对象;
- condition() - 初始化用于检查绑定的条件,无参数,返回创建的Condition对象。默认情况下,Condition对象在addNode()方法阶段初始化;
- all() - 设置始终为真条件,无参数,返回Node对象。设置此条件时,无需设置setForeign()、setParent()和setPrimary();
- equally() - 设置条件,当外键和主键的值相同时(使用严格比较)为真。初始化Condition对象时,默认设置此条件。无参数,返回Node对象;
- primaryInForeign([严格true/false]) - 设置条件,当主键的值包含在元素外键的值集合中时为真,接受一个参数,指定是严格比较(默认)还是非严格比较,返回Node对象;
- foreignInPrimary([严格true/false]) - 与primaryInForeign()类似,但将foreign和primary对调,接受一个参数,指定是严格比较(默认)还是非严格比较,返回Node对象;
- callable([可调用函数]) - 通过使用回调函数设置自定义条件,函数参数中首先传入外键的值,其次传入主键的值,函数结果应返回true - 条件为真或false。方法参数接受一个函数,并返回Node对象;
- tie() - 逻辑上完成绑定(Node对象)的构建,并初始化新的绑定,无参数,返回创建的Node对象;
- compile([verification true/false]) - 最终方法,启动编译器,参数接受容器和绑定的正确性检查(默认true - 检查启用)。方法返回编译后的数组(当使用one()选项时)或数组集。
填充容器(Container)的规则
- 根容器不能有setName()方法设置的值;
- 根容器不能有绑定(Node对象集);
- 所有容器都必须用数组填充;
- 除了根容器外,所有容器至少应有一个绑定(使用addNode()方法)或至少一个设置的反向调用函数(使用addCallable()方法);
- 地图中的每个嵌套对象都必须关联(初始化)一个容器;
- 初始化容器时,需要传递地图中嵌套对象的现有别名名称。
绑定设置规则(Node)
如果不使用condition()->all()条件
- 必须指定现有的foreign键(使用setForeign()方法);
- 必须指定现有的父嵌套对象(使用setParent()方法);
- 必须指定现有的primary键(使用setPrimary()方法)。
执行条件规则
只有当以下条件满足时,才会进行填充
- 所有绑定都为真,当使用callable()方法时,返回函数将为true;
- 所有初始化的反向调用函数(使用容器addCallable()方法)返回true;
- 当在反向调用函数中使用addCallable()方法时,如果modified->父元素的修改导致变化,则变化无论如何都会发生,而不考虑函数返回值(true/false,或无返回值)。
备注...
- 备注:计划修复糟糕的代码并扩展这个项目以支持集合和对象;
- 备注:强烈请求对此作品的构造性批评,并想知道我的这个“自行车”做得如何(寻找类似物 - 没找到)。