From 5ba44a03ea93b0e00bcf6632da1645dcf067f355 Mon Sep 17 00:00:00 2001 From: XavierGR Date: Fri, 18 Apr 2025 08:04:38 +0200 Subject: [PATCH] Add computed tag for attribute computation from OQL Expression --- core/attributedef.class.inc.php | 25 +++++++++++++++++++++++-- core/dbobject.class.php | 20 ++++++++++++++++++++ core/metamodel.class.php | 1 - setup/compiler.class.inc.php | 15 +++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 9fc7b0105..43bd99394 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -181,6 +181,23 @@ abstract class AttributeDefinition return $this->GetSearchType() != static::SEARCH_WIDGET_TYPE_RAW; } + /** + * @return bool + */ + public function IsComputed() + { + return $this->IsParam('expression'); + } + + /** + * @return array + * @throws \OQLException + */ + protected function GetComputedPrerequisiteAttributes(): array { + $oExpression = Expression::FromOQL($this->m_aParams['expression']); + return $oExpression->ListRequiredFields(); + } + /** @var string */ protected $m_sCode; /** @var array */ @@ -2717,7 +2734,11 @@ class AttributeDBFieldVoid extends AttributeDefinition public function GetPrerequisiteAttributes($sClass = null) { - return $this->Get("depends_on"); + $aPrerequisiteAttributes = $this->Get("depends_on"); + if($this->HasParam('expression')) { + $aPrerequisiteAttributes = array_merge($aPrerequisiteAttributes, $this->GetComputedPrerequisiteAttributes()); + } + return $aPrerequisiteAttributes; } public static function IsBasedOnDBColumns() @@ -2732,7 +2753,7 @@ class AttributeDBFieldVoid extends AttributeDefinition public function IsWritable() { - return !$this->IsMagic(); + return !$this->IsMagic() && !$this->IsComputed(); } public function GetSQLExpr() diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 4068819e8..0c7cd86e9 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -708,6 +708,8 @@ abstract class DBObject implements iDisplay $this->UpdateMetaAttributes(array($sAttCode)); + $this->UpdateDependentComputedAttributes($sAttCode); + // The object has changed, reset caches $this->m_bCheckStatus = null; @@ -6992,5 +6994,23 @@ abstract class DBObject implements iDisplay { return array_key_exists($sSection, $this->aContext); } + + /** + * @param string $sAttCode + * + * @return void + * @throws CoreException + * @throws OQLException + */ + private function UpdateDependentComputedAttributes(string $sAttCode): void + { + foreach (MetaModel::GetDependentAttributes(get_class($this), $sAttCode) as $sCode) { + $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sCode); + if ($oAttDef->IsComputed()) { + $oExpression = Expression::FromOQL($oAttDef->GetParams()['expression']); + $this->_Set($sCode, $this->EvaluateExpression($oExpression)); + } + } + } } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index af9301bdf..41aecd2cd 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2661,7 +2661,6 @@ abstract class MetaModel */ public static function GetAttributeFlags($sClass, $sState, $sAttCode) { - $iFlags = 0; // By default (if no life cycle) no flag at all if (self::HasLifecycle($sClass)) { $aStates = MetaModel::EnumStates($sClass); if (!array_key_exists($sState, $aStates)) { diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index a9394931a..915d30ea5 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -2134,6 +2134,7 @@ EOF $this->CompileCommonProperty('default_value', $oField, $aParameters, $sModuleRelativeDir, ''); $this->CompileCommonProperty('is_null_allowed', $oField, $aParameters, $sModuleRelativeDir, false); $this->CompileCommonProperty('allowed_values', $oField, $aParameters, $sModuleRelativeDir); + $this->CompileCommonProperty('computed', $oField, $aParameters, $sModuleRelativeDir, false); $aParameters['depends_on'] = $sDependencies; } elseif ($sAttType == 'AttributeEnum') { $this->CompileAttributeEnumValues($sModuleRelativeDir, $sClass, $sAttCode, $oField, $aParameters, $sCss); @@ -2141,6 +2142,7 @@ EOF $this->CompileCommonProperty('sql', $oField, $aParameters, $sModuleRelativeDir); $this->CompileCommonProperty('default_value', $oField, $aParameters, $sModuleRelativeDir, ''); $this->CompileCommonProperty('is_null_allowed', $oField, $aParameters, $sModuleRelativeDir, false); + $this->CompileCommonProperty('computed', $oField, $aParameters, $sModuleRelativeDir, false); $aParameters['depends_on'] = $sDependencies; } elseif ($sAttType == 'AttributeMetaEnum') { $this->CompileAttributeEnumValues($sModuleRelativeDir, $sClass, $sAttCode, $oField, $aParameters, $sCss); @@ -2269,6 +2271,7 @@ EOF $this->CompileCommonProperty('is_null_allowed', $oField, $aParameters, $sModuleRelativeDir, false); $this->CompileCommonProperty('default_value', $oField, $aParameters, $sModuleRelativeDir, ''); $this->CompileCommonProperty('allowed_values', $oField, $aParameters, $sModuleRelativeDir); + $this->CompileCommonProperty('computed', $oField, $aParameters, $sModuleRelativeDir, false); $aParameters['depends_on'] = $sDependencies; } @@ -2413,7 +2416,19 @@ EOF } $aParameters['thresholds'] = 'array('.implode(', ', $aThresholds).')'; break; + case 'computed': + $oComputed = $oField->GetOptionalElement('computed'); + if(is_null($oComputed)) { + break; + } + $sExpression = self::QuoteForPHP($oComputed->GetChildText('expression')); + if(is_null($sExpression) || $sExpression === '') { + throw new DOMFormatException("missing (or empty) mandatory tag expression under the tag '".$oField->nodeName."'"); + } + + $aParameters['expression'] = $sExpression; + break; default: return false; }