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?