Force a field to be mandatory on condition
Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.
- learning:
- Force a field to be filled on condition
- level:
- Advanced
- domains:
- PHP, Constrain
- methods:
- GetAttributeFlags, GetInitialStateAttributeFlags, IsValidAttcode, DoCheckToWrite
- min version:
- 2.3.0
This use case is just one way of forcing a field to be provided.
Check before Submission
But it does not work for DataSynchro and REST/JSON API.
Only Server in production
In this use case, we only want to ensure that all Server in production, do have a location documented.
In theory we could use the GetAttributeFlags methods, adding a
dependency between location_id
and
status
, to ensure a refresh of the form, when the
status is changed.
- itop_design / classes
-
<class id="PhysicalDevice"> <fields> <field id="location_id"> <!-- force location_id flags to be recomputed if status is changed --> <dependencies _delta="redefine"> <attribute id="org_id"/> <attribute id="status"/> </dependencies> </field> </fields> </class> <class id="Server" _delta="must_exist"> <methods> <method id="GetInitialStateAttributeFlags" _delta="define"> <static>false</static> <access>public </access> <code><![CDATA[
public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array()) { if (($sAttCode == 'location_id') && (MetaModel::IsValidAttCode(get_class($this), 'status')) && ($this->Get('status') == 'production')) { return(OPT_ATT_MANDATORY | parent::GetInitialStateAttributeFlags($sAttCode, $aReasons)); } return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons); }
]]></ code> </method> <method id="GetAttributeFlags" _delta="define"> <static>false</static> <access>public </access> <code><![CDATA[
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '') { if (($sAttCode == 'location_id') && (MetaModel::IsValidAttCode(get_class($this), 'status')) && ($this->Get('status') == 'production')) { return(OPT_ATT_MANDATORY | parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState)); } return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState); }
]]></ code> </method> </methods> </class>
Only Server and Network Device in production
Declare the above method twice, one on each class Server and NetworkDevice
Only Datacenter Devices in production
Declare the above method on class DatacenterDevice instead of Server
Check on Submission
We can also check on submission that a location was provided, if not, we prevent the creation/modification and display an error message so the user can fill the Location and submit again.
We will overwrite the method DoCheckToWrite()
of
the object class:
-
This method is invoked just before writing to database - See details of call stack.
-
The method should provide error message(s) if it encounters data incoherence.
-
Errors messages are recorded in an array $this->m_aCheckIssues[],
-
Warnings messages are recorded in an array $this->m_aCheckWarnings[],
-
When returning from this method, if there is at least one error the object is not written to database (creation or update)
-
Error and warning messages are
-
displayed to the user in interactive mode only: Console, Portal, CSV import
-
logged in itop/log/error.log depending on level of tracking for DataSynchro, REST/JSON, CLI FIXME -to be checked !-
-
Migration: No visible effect on setup, but
objects not compliant can no more be modified, until they are made
compliant. So it could prevent a datasynchro or a REST/JSON script
to update other fields for eg.
To identify faulty objects, create an audit rule to retrieve
objects not compliant to this new constrain and fix them one by one
in the UI or by CSV import.
DoCheckToWrite
method can prevent creation/modification in all cases: on the
Console, in the Portal, in CSV import, in DataSynchro and in
REST/JSON API-
From iTop 3.1.0, use EVENT_DB_CHECK_TO_WRITE
-
Before iTop 3.1.0 use the Extensibility API and put that same code into
iApplicationObjectExtension::OnCheckToWrite()
Set
values on current object in this method as it has
effectIn this use case we want to prevent a Server to be put in 'production' status without a Location to be provided.
- class:Server
-
public function DoCheckToWrite() { // Always ask the parent class to perform their own check parent::DoCheckToWrite(); // Defensive programming, ensuring that 'status' is an existing field on the current class // then checking the condition: an enum value returns code, not label, so we test the code, if (MetaModel::IsValidAttCode(get_class($this), 'status') && ($this->Get('status') == 'production')) { // AttributeExternalKey are never NULL, O is the value used when empty if (MetaModel::IsValidAttCode(get_class($this), 'location_id') && ($this->Get('location_id') == 0)) { // 'Server:Error:LocationMandatoryInProduction' must be declared as a dictionary entry $this->m_aCheckIssues[] = Dict::Format('Server:Error:LocationMandatoryInProduction'); } } }
// You may also provide a simple error message in plain text $this->m_aCheckIssues[] = 'Location is mandatory for all Servers in production';
Here the way to define a dictionary entry in XML:
- itop_design / dictionaries / dictionary@EN US / entries
-
<entry id="Server:Error:LocationMandatoryInProduction" _delta="define"> <![CDATA['Location is mandatory for all Servers in production']]> </entry>