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 atype
(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:
-
Tag the LinkedSet attribute with
<with_php_computation>true</with_php_computation>
-
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 }
- 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(); } */ }
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 atype
(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:
-
We tag the LinkedSet with
<with_php_constraint>true</with_php_constraint>
-
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')); } }
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
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.
Does deletion propagation occurs correctly if I delete a Person, will this be stopped, if the team raised a CheckToWrite issue?