diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index da05ea1fd..ff0967cf6 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -4488,7 +4488,7 @@ HTML; // Protection against reentrance (e.g. cascading the update of ticket logs) // Note: This is based on the fix made on r 3190 in DBObject::DBUpdate() - if (!MetaModel::StartReentranceProtection($this)) { + if (!MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this)) { $sClass = get_class($this); $sKey = $this->GetKey(); IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD); @@ -4507,7 +4507,7 @@ HTML; } finally { - MetaModel::StopReentranceProtection($this); + MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); } if ($this->IsModified()) { diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 9dd86ae04..1b141ae36 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -3063,7 +3063,7 @@ abstract class DBObject implements iDisplay } // Prevent DBUpdate at this point (reentrance protection) - MetaModel::StartReentranceProtection($this); + MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); $this->AfterInsert(); @@ -3086,7 +3086,7 @@ abstract class DBObject implements iDisplay // - TriggerOnObjectMention $this->ActivateOnMentionTriggers(true); - MetaModel::StopReentranceProtection($this); + MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); if ($this->IsModified()) { $this->DBUpdate(); @@ -3157,7 +3157,7 @@ abstract class DBObject implements iDisplay // Protect against reentrance (e.g. cascading the update of ticket logs) $sClass = get_class($this); $sKey = $this->GetKey(); - if (!MetaModel::StartReentranceProtection($this)) { + if (!MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this)) { IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD); @@ -3195,7 +3195,7 @@ abstract class DBObject implements iDisplay if (count($aChanges) == 0) { // Attempting to update an unchanged object - MetaModel::StopReentranceProtection($this); + MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Aborted (no change)", LogChannels::DM_CRUD); return $this->m_iKey; @@ -3411,7 +3411,7 @@ abstract class DBObject implements iDisplay } finally { - MetaModel::StopReentranceProtection($this); + MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); } if ($this->IsModified()) { diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 48b0dc39c..85fc04798 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -128,7 +128,9 @@ abstract class MetaModel /** @var string */ protected static $m_sEnvironment = 'production'; - protected static $m_aReentryProtection = []; + public const REENTRANCE_TYPE_UPDATE = 'update'; + + protected static $m_aReentranceProtection = []; /** * MetaModel constructor. @@ -6800,6 +6802,19 @@ abstract class MetaModel } $sClass = $aRow[$sClassAlias."finalclass"]; } + + // if an object is already being updated, then this method will return this object instead of recreating a new one. + // At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection, + // so to ensure that the potential modifications are correctly saved, the object currently being updated is returned. + // DBUpdate() method then will take care that all the modifications will be saved. + if (array_key_exists($sClassAlias.'id', $aRow)) { + $iKey = $aRow[$sClassAlias."id"]; + $oObject = self::GetReentranceObject(Metamodel::REENTRANCE_TYPE_UPDATE, $sClass, $iKey); + if ($oObject !== false) { + return $oObject; + } + } + return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); } @@ -6825,11 +6840,6 @@ abstract class MetaModel */ public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) { - $oObject = self::GetReentryObject($sClass, $iKey); - if ($oObject !== false) { - return $oObject; - } - $oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties); if (empty($oObject)) @@ -7567,32 +7577,33 @@ abstract class MetaModel return $oAttDef->GetStyle($sValue); } - protected static function GetReentryObject($sClass, $sKey) + protected static function GetReentranceObject($sType, $sClass, $sKey) { - if (isset(self::$m_aReentryProtection[$sClass][$sKey])) { - return self::$m_aReentryProtection[$sClass][$sKey]; + if (isset(self::$m_aReentranceProtection[$sType][$sClass][$sKey])) { + return self::$m_aReentranceProtection[$sType][$sClass][$sKey]; } return false; } /** + * @param $sType * @param \DBObject $oObject * * @return bool true if reentry possible */ - public static function StartReentranceProtection(DBObject $oObject) + public static function StartReentranceProtection($sType, DBObject $oObject) { - if (isset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()])) { + if (isset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()])) { return false; } - self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()] = $oObject; + self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()] = $oObject; return true; } - public static function StopReentranceProtection(DBObject $oObject) + public static function StopReentranceProtection($sType, DBObject $oObject) { - if (isset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()])) { - unset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()]); + if (isset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()])) { + unset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()]); } } } diff --git a/test/core/CRUD/DBObjectTest.php b/test/core/CRUD/DBObjectTest.php index 84a1e90a3..ebe558bfe 100644 --- a/test/core/CRUD/DBObjectTest.php +++ b/test/core/CRUD/DBObjectTest.php @@ -151,7 +151,7 @@ class DBObjectTest extends ItopDataTestCase $oNewPerson = MetaModel::GetObject('Person', $oPerson->GetKey()); $this->assertNotEquals($oPerson->GetObjectUniqId(), $oNewPerson->GetObjectUniqId()); - MetaModel::StartReentranceProtection($oPerson); + MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $oPerson); $oPerson->Set('email', 'test1@combodo.com'); $oPerson->DBUpdate(); @@ -161,7 +161,7 @@ class DBObjectTest extends ItopDataTestCase $oNewPerson = MetaModel::GetObject('Person', $oPerson->GetKey()); $this->assertEquals($oPerson->GetObjectUniqId(), $oNewPerson->GetObjectUniqId()); - MetaModel::StopReentranceProtection($oPerson); + MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $oPerson); } public function testObjectIsReadOnly()