Sidebar

Using iTop

Creating your iTop

iTop Customization

"How to" examples
DataModel

User Interface

Automation & Ticket management

Portal Customization

:: Version 3.2.0 ::

LinkedSet related values

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

learning:
Use of event to execute action: compute a value when a LinkedSet attribute has changed or prevent a LinkedSet attribute changes.
level:
Advanced
domains:
PHP, Automation, Event
min version:
3.1.2

Let's assume, we are consuming Stock modeled as an object class with StockMove modeled as another class related to the class with a 1:n relation.

  • The Stock object has a quantity of available goods,
  • When someone want to consume some elements of the Stock, he creates a StockMove object with the required quantity
  • Then iTop must ensure that this consumption is refused if the required quantity exceed the available goods in the Stock

How to do this:

  • Before version 3.1 of iTop, the way to do this was to compute it in the OnUpdate / OnInsert of Stock class, but this was assuming that there was no other way to create/modify or delete an object in the LinkedSet than while editing the Stock object.
  • Since version 3.1, it's quite easy to edit just the relation, while the host Stock object is in read mode, so the OnUpdate / OnInsert does not occur and the computed values is not computed !!!
    You can prevent the LinkedSet edition while the Stock object is in read, using the AttributeLinkedSet(Indirect) XML tag: <edit_when>in_host_edition</edit_when>, but this is not userfriendly…

The other alternative is described below:


Compute a LinkedSet dependent value

We need to compute something which depends on the content of a LinkedSet attribute stockmoves_list and store it on a Stock class.

  • The computation method is ComputeAndUpdateStock(), it computes then updates a Stock field remaining_quantity
  • The StockMove object has a mandatory external key to MyClass, a quantity and a type (which says if we consume or provision the Stock)
  • We want to be sure that the value is always up-to-date regardless of the way the LinkedSet content is modified.
  • A StockMove creation, modification or deletion, requires to recompute the Stock remaining_quantity.

For this, you just need to:

  1. Tag the LinkedSet attribute with <with_php_computation>true</with_php_computation>
  2. Listen to the EVENT_DB_LINKS_CHANGED event and act on it, by computing what need to be computed
itop_design / classes / class@Stock
      <fields>
        <field id="stockmoves_list" xsi:type="AttributeLinkedSet">
          <ext_key_to_me>stock_id</ext_key_to_me>
          <linked_class>StockMove</linked_class>
          <tracking_level>none</tracking_level>
          <count_min/>
          <count_max/>
          <with_php_constraint>true</with_php_constraint>
          <with_php_computation>true</with_php_computation>
          <edit_mode>none</edit_mode>
        </field>
      <fields>
      <event_listeners>
        <event_listener id="OnLinksChanged" _delta="define">
          <event>EVENT_DB_LINKS_CHANGED</event>
          <callback>OnLinksChanged</callback>
          <rank>0</rank>
        </event_listener>
      </event_listeners>
      <methods>
Stock::OnLinksChanged()
public function OnLinksChanged(Combodo\iTop\Service\Events\EventData $oEventData)
{
        $this->ComputeAndUpdateStock();
        // $this->DBUpdate(); useless on $this 
        // If ComputeAndUpdateStock() do modify $this, then iTop will execute DBUpdate() automatically
}
The OnUpdate() past computation becomes useless, so remove the computation part of it
Stock::OnUpdate()
protected function OnUpdate()
    {
        $aChanges = $this->ListChanges();
 
        /* Do not compute as the EVENT_DB_LINKS_CHANGED will be called once and will do it
        if (array_key_exists('stockmoves_list', $aChanges)) {
            $this->ComputeAndUpdateStock();
        } */
    }
In iTop 3.2, there is no means to know which LinkedSet(s) has triggered the event EVENT_DB_LINKS_CHANGED.
If you have more than one LinkedSet attributes on the MyClass object with the tag <with_php_computation>true</with_php_computation>
Then you can use the same OnLinksChanged method, to call ComputeAndUpdateStock() and ComputeAndUpdateConsumers() for eg.

Prevent LinkedSet invalid changes

Reminder: Overall goal and context, we have Stock modeled as an object class with StockMove modeled as another class related to the Stock with a 1:n relation.

  • The Stock object has a quantity of available goods,
  • When someone want to consume some elements of the Stock, he creates a StockMove object with the required quantity
  • Then iTop must ensure that this consumption is refused if the required quantity exceed the available goods in the Stock

This part: address the way we can prevent an excess of consumption

To prevent the action on the LinkedSet stockmoves_list on a condition which can only be checked by the host object:

  • We have a method ComputeStock() which computes the Stock theoretical “remaining quantity” but does not update the field remaining_quantity, it's just a check.
  • As in the above example, the StockMove object has a mandatory external key to MyClass, a quantity and a type (which says if we consume or provision the Stock)
  • This time, we want to prevent the creation/modification/deletion of a StockMove which would result into a negative value in the Stock.

For this, you just need to:

  1. We tag the LinkedSet with <with_php_constraint>true</with_php_constraint>
  2. Listen to the EVENT_DB_CHECK_TO_WRITE event, check the condition on the LinkedSet and if needed prevent the link creation/modification/deletion to occur, by adding a Check Issue.
itop_design / classes / class@Stock
      <fields>
        <field id="stockmoves_list" xsi:type="AttributeLinkedSet">
          <ext_key_to_me>stock_id</ext_key_to_me>
          <linked_class>StockMove</linked_class>
          <tracking_level>none</tracking_level>
          <count_min/>
          <count_max/>
          <with_php_constraint>true</with_php_constraint>
          <with_php_computation>true</with_php_computation>
          <edit_mode>none</edit_mode>
        </field>
      <fields>
      <event_listeners>
        <event_listener id="CheckStockAvailability" _delta="define">
          <event>EVENT_DB_CHECK_TO_WRITE</event>
          <callback>CheckStockAvailability</callback>
          <rank>0</rank>
        </event_listener>
      </event_listeners>
Stock::CheckStockAvailability()
public function CheckStockAvailability()
{
        // Stop if required quantity is not available in stock
        // ComputeStock() does not modify $this, it just compute
        if ($this->ComputeStock() < 0 ) {  
            // Adding a CheckIssue will block/prevent the Linked object creation/modification or deletion
            $this->AddCheckIssue(Dict::Format('Stock:Error:NotEnoughQuantityInStock'));
        }
}
The linked object is the lnk in case of LinkedSetIndirect attribute
Otherwise it is the remote object itself in case of LinkedSet attribute

Other Usecase

  • In an Enclosure we can define its total capacity in U size.
  • The Enclosure contains Devices which have also a U size.
  • A Device is located in a single Enclosure.
  • The devices_list on the Enclosure is a LinkedSet attribute.
  • You can prevent the sum of installed Devices U to exceed the enclosure U size, using the above method.

Limitations

Currently you cannot restrict (with iTop 3.2 at least) in which situations, the check will be performed. It's going to be checked every time there is a change to a Device of the Enclosure, even if that change is unrelated to the Device U field.
You cannot prevent the modification of a remote object linked through an n:n relationship, using the above method.

Let's take a stupid example, but one which can be understood. Let says that on a team we want to ensure that there are the “same number of men and women plus or minus two” in the team members.

  • The rule will be checked automatically each time a member is added or removed, preventing the addition or the removal when the rule would be broken by the change.
  • But I will be able to defeat the rule, by changing the gender of one of its team member, as this change will not trigger the Team Check.

FIXME Does deletion propagation occurs correctly if I delete a Person, will this be stopped, if the team raised a CheckToWrite issue?

3_2_0/customization/event-db-links-changed.txt · Last modified: 2024/09/10 10:25 (external edit)
Back to top
Contact us