Sidebar

Using iTop

Creating your iTop

iTop Customization

"How to" examples
DataModel

User Interface

Automation & Ticket management

Portal Customization

Calculated field on 1:n

Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.

learning:
Update an object based on related objects
level:
Advanced
domains:
PHP, Automation
min version:
3.2.0

Assuming you want to define a “calculated” field on Class A, which would depend on data defined on class(es) B object(s), something more complex than just an ExternalField. For examples:

  1. On a Ticket, you want the sum time spent on all of its WorkOrders (1:n)
  2. On a Ticket, you want the count of manually added CIs (n:n)
  3. On FunctionalCI, you want a Location field, which would be simply the location for PhysicalDevice, and a computed location on Logical Device, based on the location of the Physical device on which the Logical is running.

You want to be able to search on that “calculated” field.
You want to be able to run audit against that “calculated” field.

Note that such “calculated” field induces risks on performance while updating large number of Class B objects

Simple Strategy

Here is the generic strategy to implement such “calculated” field which aggregates any field of a one-to-many relationship or just count a many-to-many relationship

  1. To be able to query on it, we must store it in database, so make it a persistent field.
  2. In order to ensure that it is always accurate, you need to identify when it must be recomputed and do it. For this we will define/overwrite those functions:

For more complex computed field.

On Class A

  • Write a method to calculate the “calculated” field value based on other fields / related objects. In some case it can directly be used as a callback of EVENT_DB_LINKS_CHANGED
  • Callback method on EVENT_DB_SET_INITIAL_ATTRIBUTE_FLAGS: to specify that the “calculated” field is hidden in creation form
  • Callback method on EVENT_DB_SET_ATTRIBUTE_FLAGS: to specify that the “calculated” field is read-only on modification
  • Callback of EVENT_DB_LINKS_CHANGED which compute the calculated field and set it on the object
  • Force the with_php_computation XML flag of the LinkedSet or LinkedSetIndirect field to “true”

Nothing to do on remote class, as we used to do before we add events and the with_php_computation XML flag

Use Case: Sum 1:n

In this use case, we want to Sum on a Ticket the timespent on all WorkOrders of this Ticket.

  • First we need to add a time_spent integer attribute on WorkOrder and Ticket classes
  • In order to ensure that Ticket::time_spent is always accurate, we need to identify when it must be recomputed:
    • When the Ticket is updated
    • When a WorkOrder is created → we need to recompute its Ticket
    • When a WorkOrder is updated → we need to recompute its former and its new Ticket
    • When a WorkOrder is deleted → we need to recompute its former Ticket
  • Then define/overwrite those functions:

On Class Ticket

Event Listeners

  • EVENT_DB_BEFORE_WRITE ⇒ BeforeWrite()
  • EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS ⇒ HideTimeSpent()
  • EVENT_DB_SET_ATTRIBUTES_FLAGS ⇒ MakeTimeSpentReadOnly()
  • EVENT_DB_LINKS_CHANGED ⇒ ComputeTimeSpent()
itop_design / classes
    <class id="Ticket" _delta="must_exist">
      <fields>
        <field id="workorders_list">
          <with_php_computation _delta="force">true</with_php_computation>
        </field>
      </fields>
    </class>

Callbacks methods

  • ComputeTimeSpent(): calculate the “time_spent” field value based on “time_spent” of WorOrders.
  • BeforeWrite(): Workorders cannot be modified at the same time as a Ticket in the default datamodel, but if this is enable with the edit mode then this function is needed.
  • HideTimeSpent(): to specify that the “calculated” field is hidden on Ticket creation form
  • MakeTimeSpentReadOnly(): to specify that the “calculated” field is read-only on Ticket modification form
class::Ticket
protected function ComputeTimeSpent(?Combodo\iTop\Service\Events\EventData $oEventData)
{
   $iSum = 0;
   $oWorkOrderSet = $this->Get('workorders_list');
   while($oWorkOrder = $oWorkOrderSet->Fetch()) {
        $iSum += $oWorkOrder->Get('time_spent');
   }
   $this->Set('time_spent', $iSum);
}
public function BeforeWrite(Combodo\iTop\Service\Events\EventData $oEventData)
{
  $aChanges = $this->ListChanges();
  if (array_key_exists('workorders_list', $aChanges)) {
      $this->ComputeTimeSpent(null);
  }
}
public function HideTimeSpent(Combodo\iTop\Service\Events\EventData $oEventData)
{   
    $this->ForceInitialAttributeFlags('time_spent', OPT_ATT_HIDDEN ); 
}
public function MakeTimeSpentReadOnly(Combodo\iTop\Service\Events\EventData $oEventData)
{   
    $this->ForceAttributeFlags('time_spent', OPT_ATT_READONLY); 
}
latest/customization/cascade-update.txt · Last modified: 2025/05/21 11:01 by 127.0.0.1
Back to top
Contact us