You are browsing the documentation for iTop 3.0 which is not the current version.

Consider browsing to iTop 3.1 documentation

Add a 1:1 relationship

Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.
We also assume that you are familiar with dashboard design within iTop and OQL.

learning:
Add a one to one relationship attribute on a class
level:
Medium
domains:
XML, PHP, Relation
min version:
2.6.0

You want to create a field which would be a one to one relationship, but such type of field does not exist, so how could you do.

  • This solution is just one of the possible options.
  • This solution has some limitation: the relationship will be editable on one side only
  • The relation must be visible from both side of the relationship.
  • In this solution, I have considered that it was not possible to create a relationship with someone already engaged, no forced divorce 💑🤪!, but this is an option, you could allow it

1:1 between 2 classes

Declare 2 ExternalKeys

Let's imagine 2 classes: Person and CompanyCar. A Person may have a company car, but only one. A company car can be allocated to one Person maximum.

itop-design / classes / class@Person / fields
   <field id="companycar_id" xsi:type="AttributeExternalKey" _delta="define">
       <filter><![CDATA[SELECT CompanyCar AS cc WHERE cc.person_id = 0]]></filter>
       <dependencies/>
       <sql>companycar_id</sql>
       <target_class>CompanyCar</target_class>
       <is_null_allowed>true</is_null_allowed>
       <on_target_delete>DEL_MANUAL</on_target_delete>
       <allow_target_creation>true</allow_target_creation>
    </field>
itop-design / classes / class@CompanyCar/ fields
   <field id="person_id" xsi:type="AttributeExternalKey" _delta="define">
       <filter/>
       <dependencies/>
       <sql>person_id</sql>
       <target_class>Person</target_class>
       <is_null_allowed>true</is_null_allowed>
       <on_target_delete>DEL_MANUAL</on_target_delete>
       <allow_target_creation>false</allow_target_creation>
    </field>

Force Keys synchronization

Replace in the code below the constants by your own classes names and field code and that's all

Person
class SynchroOneToOneRelation implements iApplicationObjectExtension
{ 
    const CURRENT_CLASS = 'Person';
    const KEY_TO_CURRENT = 'person_id';
    const REMOTE_CLASS = 'CompanyCar';
    const KEY_TO_REMOTE = 'companycar_id';
 
    public function OnDBInsert($oObject, $oChange = null)
    {
        if ((get_class($oObject) == static::CURRENT_CLASS ) && ($oObject->Get(static::KEY_TO_REMOTE) > 0))
        {
            $oNewRemote = MetaModel::GetObject(static::REMOTE_CLASS, $oObject->Get(static::KEY_TO_REMOTE), false, true);
            if ($oNewRemote) {
                $oNewRemote->Set(static::KEY_TO_CURRENT, $oObject->GetKey());
                $oNewRemote->DBUpdate();
            }
        }
    }
    public function OnDBUpdate($oObject, $oChange = null)
    {
        if (get_class($oObject) == static::CURRENT_CLASS ) {
            $aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
            if (array_key_exists(static::KEY_TO_REMOTE, $aChanges)) {
                if ($oObject->Get(static::KEY_TO_REMOTE) > 0) {
                    $oNewRemote = MetaModel::GetObject(static::REMOTE_CLASS, $oObject->Get(static::KEY_TO_REMOTE), false, true);
                    if ($oNewRemote) {
                        $oNewRemote->Set(static::KEY_TO_CURRENT, $oObject->GetKey());
                        $oNewRemote->DBUpdate();
                    }
                }
                if ($aChanges[static::KEY_TO_REMOTE] > 0) {
                    $oOldRemote = MetaModel::GetObject(static::REMOTE_CLASS, $aChanges[static::KEY_TO_REMOTE], false, true);
                    if ($oOldRemote) {
                        $oOldRemote->Set(static::KEY_TO_CURRENT, 0);
                        $oOldRemote->DBUpdate();
                    }
                }
            }
        }
    }
    public function OnDBDelete($oObject, $oChange = null)
    {
        if (get_class($oObject) == static::CURRENT_CLASS && ($oObject->Get(static::KEY_TO_REMOTE) > 0)) {
            $oOldRemote = MetaModel::GetObject(static::REMOTE_CLASS, $oObject->Get(static::KEY_TO_REMOTE), false, true);
            if ($oOldRemote) {
                $oOldRemote->Set(static::KEY_TO_CURRENT, 0);
                $oOldRemote->DBUpdate();
            }
        }    
    }
    public function OnCheckToWrite($oObject)
    {
        return;
    }
    public function OnIsModified($oObject)
    {
        return false;
    }    
    public function OnCheckToDelete($oObject)
    {
        return;
    }  
}

Prevent edition on one side

In order to avoid an infinite loop of update, it's easier to lock the edition on one side of the relationship

CompanyCar
  public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
  {
     if ($sAttCode == 'person_id')
     {
         return(OPT_ATT_READONLY | parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState));
     }
     return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
  }
  public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
  {
     if (($sAttCode == 'person_id'))
     {
         return(OPT_ATT_HIDDEN | parent::GetInitialStateAttributeFlags($sAttCode, $aReasons));
     }
     return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
  }

Allowing stealing

Let's suppose we want to reaffect a car to a driver, even if the car is already allocated:

  1. Change the filter to allow stealing a company car to its current driver,
  2. But when you update the previous driver, to empty his companycar_id, you must not enter in an infinite loop.

FIXME Find and describe a mechanism to avoid looping: for example storing on the OldDriver object an on the flight property like _no_propagation_on_companycar_id and test it in OnDBUpdate to stop the loop

1:1 within the same class

Let's suppose that we have a Person field called In couple with pointing to another person, but himself.

FIXME requires a bit of thinking to avoid infinite loop as we must allow edition on both ends as there is just one.

3_0_0/customization/one-to-one-relation.txt · Last modified: 2022/04/06 18:28 (external edit)
Back to top
Contact us