二次开发-调整工单-多字段运算
多字段运算和级联更新
先决条件:您必须熟悉教程中使用的语法 并且已经创建了一个扩展.
学习:根据相关对象更新对象
水平:[ ttps://]高级
域:[ ttps://]PHP, [ ttps://]Automation
最低版本:2.3.0
假设您要在A类上定义一个“计算”字段,这依赖于在B类对象上定义的数据,这比仅一个ExternalField要复杂得多。举些例子:
- 在工单上,您需要花费在所有WorkOrder上的时间
- 在工单上,您需要手动添加的配置项的计数。
- 在FunctionalCI上,您需要一个Location字段,该字段仅是PhysicalDevice的位置,以及基于Logical正在运行的物理设备的位置在Logical Device上的计算位置。
- …
您希望能够在“已计算”字段上使用搜索。
您希望能够针对该“计算”字段运行审计。
请注意,在更新大量B类对象时,此类“计算”字段会在性能上引发风险
这是实现这种“计算”字段的通用策略。
- 为了能够在其上使用查询,我们必须将其存储在数据库中,因此使其成为持久字段。
- 为了确保它总是准确的,您需要确定何时必须重新计算它并执行。为此,我们将定义覆盖这些功能:
在A班
- ComputeValues():根据其他与字段相关的对象,计算“已计算”字段价值。
- GetInitialStateAttributeFlags():指定在创建表单中隐藏“计算所得”字段
- GetAttributeFlag():指定修改时“计算所得”字段为只读
在依赖B类上
- OnUpdate(),OnDelete():检查字段是否已被修改(这将使影响度为A类的“计算”字段),设置一个内部变量以在AfterUpdate()AfterDelete()中记住它。
- AfterInsert,AfterUpdate(),AfterDelete:如果需要(基于上面设置的内部变量),则对具有受“变更”影响的“已计算”字段的所有对象A强制重新计算。
提示与解释
- 我们使用AfterXXX()将变更设置为级联,而不使用OnXXX(),因为否则远程对象尚未保存在数据库中,因此基于ComputeValues的计算将使用数据库中发现的不准确的数据。
- 我们使用OnUpdate()来检查是否需要级联和变更,因为在更新后删除数组$ this→ListChanges()为空。
- 通常会调用ComputeValues(),但是在将对象加载到内存中时不会调用。
用例:总和
在此用例中,我们希望在工单上求和该工单的所有工作订单上花费的时间。
- 首先,我们需要在WorkOrder和工单类上添加一个time_spent整数属性
- 为了确保工单:: time_spent始终准确,我们需要确定何时必须重新计算它:
- 工单更新时
- 创建工作订单后→我们需要重新计算其工单
- 更新工作订单后→我们需要重新计算其先前版本和新版本工单
- 删除工作订单后→我们需要重新计算其以前的工单
- 然后定义重写这些功能:
在工单类上
- ComputeTimeSpent():基于WorOrders的“ time_spent”来计算价值的“ time_spent”字段。
- OnUpdate():无法从默认数据模型中的工单修改工作单列表,但是如果在编辑模式下启用了该工作单,则需要此职能。
- GetInitialStateAttributeFlags():指定在工单创建表单上隐藏“计算的”字段
- GetAttributeFlag():指定在工单修改时,“计算的”字段为只读
[ ttps://]class::Ticket
protected function ComputeTimeSpent()
{
$iSum = 0;
$oWorkOrderSet = $this->Get('workorders_list');
while($oWorkOrder = $oWorkOrderSet->Fetch())
{
$iSum += $oWorkOrder->Get('time_spent');
}
$this->Set('time_spent', $iSum);
}
protected function OnUpdate()
{
$aChanges = $this->ListChanges();
if (('workorders_list', $aChanges))
{
$this->ComputeTimeSpent();
}
}
public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = ())
{
// Hide the calculated field in object creation form
if (($sAttCode == 'time_spent'))
return(OPT_ATT_HIDDEN | parent::GetInitialStateAttributeFlags($sAttCode,
$aReasons));
return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
}
public function GetAttributeFlags($sAttCode, &$aReasons = (), $sTargetState = '')
{
// Force the computed field to be read-only, preventing it to be written
if (($sAttCode == 'time_spent'))
return(OPT_ATT_READONLY | parent::GetAttributeFlags($sAttCode, $aReasons,
$sTargetState));
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
- OnUpdate,OnDelete:如果字段工单_id已被修改,则暂时存储先前的价值。
- AfterInsert,AfterUpdate,AfterDelete:强制重新计算当前和以前的工单。
[ ttps://]class::WorkOrder protected function UpdateTicket($id) { if ($id != 0) { $oObject = MetaModel::GetObject('Ticket', $id, false); // FYI: MetaModel::GetObject('Ticket', 0); generates a FatalError if (($oObject)) // in case the user is not allowed to see the object { $oObject->ComputeTimeSpent(); $oObject->DBUpdate(); } } } protected function OnUpdate() { $aChanges = $this->ListChanges(); if (('ticket_id', $aChanges)) { // store in the WorkOrder memory object the previous value $this->iPreviousTicketId = $this->GetOriginal('ticket_id'); } if (('time_spent', $aChanges)) { // record in the WorkOrder memory object that time spent was changed $this->bTimeChanged = true; } } protected function AfterUpdate() { // The WorkOrder is updated in DB and Time spent was changed, if (($this->bTimeChanged)) { // we need to recompute TimeSpent on the Ticket $this->UpdateTicket($this->Get('ticket_id')); } // If there was a "former" Ticket then we also need to update it if (($this->iPreviousTicketId )) $this->UpdateTicket($this->iPreviousTicketId); } protected function AfterInsert() { // A new Workorder was created with time_spent, so let's recompute the Ticket if ($this->Get('time_spent') > 0) $this->UpdateTicket($this->Get ('ticket_id')); } protected function OnDelete() { // If the deleted Workorder had some time spent set, // then let's flag that a recomputation is needed, and store the former Ticket id for later use if ($this->GetOriginal('time_spent') > 0) { $this->iPreviousTicketId = $this->GetOriginal('ticket_id'); } } protected function AfterDelete() { // If a recomputation of the former Ticket is needed, let's do it if (($this->iPreviousTicketId )) $this->UpdateTicket($this->iPreviousTicketI
通过检查是否在OnXXXX()职能中修改了WorkOrder :: time_spent,然后设置一个标志并在AfterXXX()中使用它以仅在真正需要时才调用UpdateTicket()来改进此示例。