Internal:

- code refactoring to generalize attributes based on an OQL expression (friendly name, obsolescence flag, ....). The intermediate class AttributeComputedFieldVoid has been swept in favor of the use of a new method: IsBasedOnOQLExpresssion.
- added an introspection API (experimental), allowing an external application to request for information about the capabilities of the framework (first step: list attributes and their main characteristics)

SVN:trunk[4720]
This commit is contained in:
Romain Quetiez
2017-05-05 15:08:49 +00:00
parent b707db9364
commit a8ad3004ea
5 changed files with 355 additions and 247 deletions

View File

@@ -226,20 +226,93 @@ abstract class AttributeDefinition
{
return $this;
}
public function IsDirectField() {return false;}
public function IsScalar() {return false;}
public function IsLinkSet() {return false;}
public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;}
public function IsHierarchicalKey() {return false;}
public function IsExternalField() {return false;}
/**
* Deprecated - use IsBasedOnDBColumns instead
* @return bool
*/
public function IsDirectField() {return static::IsBasedOnDBColumns();}
/**
* Returns true if the attribute value is built after DB columns
* @return bool
*/
static public function IsBasedOnDBColumns() {return false;}
/**
* Returns true if the attribute value is built after other attributes by the mean of an expression
* @return bool
*/
static public function IsBasedOnOQLExpression() {return false;}
/**
* Returns true if the attribute value can be shown as a string
* @return bool
*/
static public function IsScalar() {return false;}
/**
* Returns true if the attribute value is a set of related objects (1-N or N-N)
* @return bool
*/
static public function IsLinkSet() {return false;}
/**
* Returns true if the attribute is an external key, either directly (RELATIVE to the host class), or indirectly (ABSOLUTELY)
* @return bool
*/
public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;}
/**
* Returns true if the attribute value is an external key, pointing to the host class
* @return bool
*/
static public function IsHierarchicalKey() {return false;}
/**
* Returns true if the attribute value is stored on an object pointed to be an external key
* @return bool
*/
static public function IsExternalField() {return false;}
/**
* Returns true if the attribute can be written (by essence)
* @return bool
*/
public function IsWritable() {return false;}
/**
* Returns true if the attribute has been added automatically by the framework
* @return bool
*/
public function IsMagic() {return $this->GetOptional('magic', false);}
public function LoadInObject() {return true;}
public function LoadFromDB() {return true;}
/**
* Returns true if the attribute value is kept in the loaded object (in memory)
* @return bool
*/
static public function LoadInObject() {return true;}
/**
* Returns true if the attribute value comes from the database in one way or another
* @return bool
*/
static public function LoadFromDB() {return true;}
/**
* Returns true if the attribute should be loaded anytime (in addition to the column selected by the user)
* @return bool
*/
public function AlwaysLoadInTables() {return $this->GetOptional('always_load_in_tables', false);}
public function GetValue($oHostObject){return null;} // must return the value if LoadInObject returns false
public function IsNullAllowed() {return true;}
public function GetCode() {return $this->m_sCode;}
/**
* Must return the value if LoadInObject returns false
* @return mixed
*/
public function GetValue($oHostObject){return null;}
/**
* Returns true if the attribute must not be stored if its current value is "null" (Cf. IsNull())
* @return bool
*/
public function IsNullAllowed() {return true;}
/**
* Returns the attribute code (identifies the attribute in the host class)
* @return string
*/
public function GetCode() {return $this->m_sCode;}
/**
* Find the corresponding "link" attribute on the target class, if any
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute() {return null;}
/**
@@ -774,8 +847,8 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetEditClass() {return "LinkedSet";}
public function IsWritable() {return true;}
public function IsLinkSet() {return true;}
public function IsWritable() {return true;}
static public function IsLinkSet() {return true;}
public function IsIndirect() {return false;}
public function GetValuesDef() {return $this->Get("allowed_values");}
@@ -899,7 +972,7 @@ class AttributeLinkedSet extends AttributeDefinition
}
if ($sAttCode == $this->GetExtKeyToMe()) continue;
if ($oAttDef->IsExternalField()) continue;
if (!$oAttDef->IsDirectField()) continue;
if (!$oAttDef->IsBasedOnDBColumns()) continue;
if (!$oAttDef->IsScalar()) continue;
$sAttValue = $oObj->GetAsCSV($sAttCode, $sSepValue, '', $bLocalize);
if (strlen($sAttValue) > 0)
@@ -1162,7 +1235,7 @@ class AttributeLinkedSet extends AttributeDefinition
}
if ($sAttCode == $this->GetExtKeyToMe()) continue;
if ($oAttDef->IsExternalField()) continue;
if (!$oAttDef->IsDirectField()) continue;
if (!$oAttDef->IsBasedOnDBColumns()) continue;
if (!$oAttDef->IsScalar()) continue;
$attValue = $oObj->Get($sAttCode);
$aAttributes[$sAttCode] = $oAttDef->GetForJSON($attValue);
@@ -1265,9 +1338,8 @@ class AttributeLinkedSet extends AttributeDefinition
}
/**
* Find the corresponding "link" attribute on the target class
*
* @return string The attribute code on the target class, or null if none has been found
* Find the corresponding "link" attribute on the target class, if any
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute()
{
@@ -1349,9 +1421,8 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
}
/**
* Find the corresponding "link" attribute on the target class
*
* @return string The attribute code on the target class, or null if none has been found
* Find the corresponding "link" attribute on the target class, if any
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute()
{
@@ -1416,8 +1487,8 @@ class AttributeDBFieldVoid extends AttributeDefinition
public function GetValuesDef() {return $this->Get("allowed_values");}
public function GetPrerequisiteAttributes($sClass = null) {return $this->Get("depends_on");}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
static public function IsBasedOnDBColumns() {return true;}
static public function IsScalar() {return true;}
public function IsWritable() {return !$this->IsMagic();}
public function GetSQLExpr()
{
@@ -4508,9 +4579,8 @@ class AttributeExternalKey extends AttributeDBFieldVoid
}
/**
* Find the corresponding "link" attribute on the target class
*
* @return string The attribute code on the target class, or null if none has been found
* Find the corresponding "link" attribute on the target class, if any
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute()
{
@@ -4617,7 +4687,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
parent::SetHostClass($sHostClass);
}
public function IsHierarchicalKey() {return true;}
static public function IsHierarchicalKey() {return true;}
public function GetTargetClass($iType = EXTKEY_RELATIVE) {return $this->m_sTargetClass;}
public function GetKeyAttDef($iType = EXTKEY_RELATIVE){return $this;}
public function GetKeyAttCode() {return $this->GetCode();}
@@ -4706,9 +4776,8 @@ class AttributeHierarchicalKey extends AttributeExternalKey
}
/**
* Find the corresponding "link" attribute on the target class
*
* @return string The attribute code on the target class, or null if none has been found
* Find the corresponding "link" attribute on the target class, if any
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute()
{
@@ -4808,7 +4877,7 @@ class AttributeExternalField extends AttributeDefinition
return $this->GetKeyAttDef($iType)->GetTargetClass();
}
public function IsExternalField() {return true;}
static public function IsExternalField() {return true;}
public function GetKeyAttCode() {return $this->Get("extkey_attcode");}
public function GetExtAttCode() {return $this->Get("target_attcode");}
@@ -4868,11 +4937,10 @@ class AttributeExternalField extends AttributeDefinition
return $oExtAttDef->IsNullAllowed();
}
public function IsScalar()
static public function IsScalar()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->IsScalar();
}
return true;
}
public function GetFilterDefinitions()
{
@@ -5054,10 +5122,10 @@ class AttributeBlob extends AttributeDefinition
}
public function GetEditClass() {return "Document";}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
public function IsWritable() {return true;}
static public function IsBasedOnDBColumns() {return true;}
static public function IsScalar() {return true;}
public function IsWritable() {return true;}
public function GetDefaultValue(DBObject $oHostObject = null) {return "";}
public function IsNullAllowed(DBObject $oHostObject = null) {return $this->GetOptional("is_null_allowed", false);}
@@ -5412,9 +5480,9 @@ class AttributeStopWatch extends AttributeDefinition
}
public function GetEditClass() {return "StopWatch";}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
static public function IsBasedOnDBColumns() {return true;}
static public function IsScalar() {return true;}
public function IsWritable() {return true;}
public function GetDefaultValue(DBObject $oHostObject = null) {return $this->NewStopWatch();}
@@ -6092,15 +6160,15 @@ class AttributeSubItem extends AttributeDefinition
public function GetEditClass() {return "";}
public function GetValuesDef() {return null;}
public function GetValuesDef() {return null;}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
public function IsWritable() {return false;}
static public function IsBasedOnDBColumns() {return true;}
static public function IsScalar() {return true;}
public function IsWritable() {return false;}
public function GetDefaultValue(DBObject $oHostObject = null) {return null;}
// public function IsNullAllowed() {return false;}
public function LoadInObject() {return false;} // if this verb returns false, then GetValue must be implemented
static public function LoadInObject() {return false;} // if this verb returns false, then GetValue must be implemented
/**
* Used by DBOBject::Get()
@@ -6245,10 +6313,10 @@ class AttributeOneWayPassword extends AttributeDefinition
}
public function GetEditClass() {return "One Way Password";}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
public function IsWritable() {return true;}
static public function IsBasedOnDBColumns() {return true;}
static public function IsScalar() {return true;}
public function IsWritable() {return true;}
public function GetDefaultValue(DBObject $oHostObject = null) {return "";}
public function IsNullAllowed() {return $this->GetOptional("is_null_allowed", false);}
@@ -6619,104 +6687,104 @@ class AttributePropertySet extends AttributeTable
*
* @package iTopORM
*/
class AttributeComputedFieldVoid extends AttributeDefinition
{
static public function ListExpectedParams()
/**
* The attribute dedicated to the friendly name automatic attribute (not written)
*
* @package iTopORM
*/
class AttributeFriendlyName extends AttributeDefinition
{
public function __construct($sCode, $sExtKeyAttCode)
{
return array_merge(parent::ListExpectedParams(), array());
$this->m_sCode = $sCode;
$aParams = array();
$aParams["default_value"] = '';
$aParams["extkey_attcode"] = $sExtKeyAttCode;
parent::__construct($sCode, $aParams);
$this->m_sValue = $this->Get("default_value");
}
public function GetEditClass() {return "";}
public function GetValuesDef() {return null;}
public function GetValuesDef() {return null;}
public function GetPrerequisiteAttributes($sClass = null) {return $this->GetOptional("depends_on", array());}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
public function IsWritable() {return false;}
public function GetSQLExpr()
{
return null;
}
public function GetDefaultValue(DBObject $oHostObject = null) {return $this->MakeRealValue("", $oHostObject);}
static public function IsScalar() {return true;}
public function IsNullAllowed() {return false;}
//
// protected function ScalarToSQL($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '')
{
$sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property
}
return array('' => $sPrefix);
return array('' => $sPrefix);
}
public function FromSQLToValue($aCols, $sPrefix = '')
static public function IsBasedOnOQLExpression() {return true;}
public function GetOQLExpression()
{
return null;
return static::GetExtendedNameExpression($this->GetHostClass());
}
public function GetSQLValues($value)
/**
* Get the friendly name for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same name expression
* Used when querying a parent class
*/
static protected function GetExtendedNameExpression($sClass)
{
return array();
}
public function GetSQLColumns($bFullSpec = false)
{
return array();
}
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
}
public function GetBasicFilterOperators()
{
return array("="=>"equals", "!="=>"differs from");
}
public function GetBasicFilterLooseOperator()
{
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode)
// 1st step - get all of the required expressions (instantiable classes)
// and group them using their OQL representation
//
$aFNExpressions = array(); // signature => array('expression' => oExp, 'classes' => array of classes)
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sSubClass)
{
case '!=':
return $this->GetSQLExpr()." != $sQValue";
break;
case '=':
default:
return $this->GetSQLExpr()." = $sQValue";
if (($sSubClass != $sClass) && MetaModel::IsAbstract($sSubClass)) continue;
$oSubClassName = MetaModel::GetNameExpression($sSubClass);
$sSignature = $oSubClassName->Render();
if (!array_key_exists($sSignature, $aFNExpressions))
{
$aFNExpressions[$sSignature] = array(
'expression' => $oSubClassName,
'classes' => array(),
);
}
$aFNExpressions[$sSignature]['classes'][] = $sSubClass;
}
// 2nd step - build the final name expression depending on the finalclass
//
if (count($aFNExpressions) == 1)
{
$aExpData = reset($aFNExpressions);
$oNameExpression = $aExpData['expression'];
}
else
{
$oNameExpression = null;
foreach ($aFNExpressions as $sSignature => $aExpData)
{
$oClassListExpr = ListExpression::FromScalars($aExpData['classes']);
$oClassExpr = new FieldExpression('finalclass', $sClass);
$oClassInList = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
if (is_null($oNameExpression))
{
$oNameExpression = $aExpData['expression'];
}
else
{
$oNameExpression = new FunctionExpression('IF', array($oClassInList, $aExpData['expression'], $oNameExpression));
}
}
}
return $oNameExpression;
}
public function IsPartOfFingerprint() { return false; }
}
/**
* The attribute dedicated to the friendly name automatic attribute (not written)
*
* @package iTopORM
*/
class AttributeFriendlyName extends AttributeComputedFieldVoid
{
public function __construct($sCode, $sExtKeyAttCode)
{
$this->m_sCode = $sCode;
$aParams = array();
// $aParams["is_null_allowed"] = false,
$aParams["default_value"] = '';
$aParams["extkey_attcode"] = $sExtKeyAttCode;
parent::__construct($sCode, $aParams);
$this->m_sValue = $this->Get("default_value");
}
public function GetKeyAttCode() {return $this->Get("extkey_attcode");}
@@ -6759,23 +6827,12 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
return $sLabel;
}
// n/a, the friendly name is made of a complex expression (see GetNameSpec)
protected function GetSQLCol($bFullSpec = false) {return "";}
public function FromSQLToValue($aCols, $sPrefix = '')
{
$sValue = $aCols[$sPrefix];
return $sValue;
}
/**
* Encrypt the value before storing it in the database
*/
public function GetSQLValues($value)
{
return array();
}
public function IsWritable()
{
return false;
@@ -6785,7 +6842,7 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
return true;
}
public function IsDirectField()
static public function IsBasedOnDBColumns()
{
return false;
}
@@ -6836,6 +6893,16 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
return '';
}
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
}
public function GetBasicFilterOperators()
{
return array("="=>"equals", "!="=>"differs from");
}
public function GetBasicFilterLooseOperator()
{
return "Contains";
@@ -7268,7 +7335,7 @@ class AttributeCustomFields extends AttributeDefinition
public function GetEditClass() {return "CustomFields";}
public function IsWritable() {return true;}
public function LoadFromDB() {return false;} // See ReadValue...
static public function LoadFromDB() {return false;} // See ReadValue...
public function GetDefaultValue(DBObject $oHostObject = null)
{

View File

@@ -1999,7 +1999,7 @@ abstract class DBObject implements iDisplay
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true;
if ($oAttDef->IsDirectField())
if ($oAttDef->IsBasedOnDBColumns())
{
$aDBChanges[$sAttCode] = $aChanges[$sAttCode];
}

View File

@@ -1677,66 +1677,71 @@ class DBObjectSearch extends DBSearch
}
$aFNJoinAlias = array(); // array of (subclass => alias)
if (array_key_exists('friendlyname', $aExpectedAtts))
foreach ($aExpectedAtts as $sAttCode => $oExpression)
{
// To optimize: detect a restriction on child classes in the condition expression
// e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
$oNameExpression = self::GetExtendedNameExpression($sClass);
$aNameFields = array();
$oNameExpression->GetUnresolvedFields('', $aNameFields);
$aTranslateNameFields = array();
foreach($aNameFields as $sSubClass => $aFields)
if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) continue;
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsBasedOnOQLExpression())
{
foreach($aFields as $sAttCode => $oField)
{
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
if ($oAttDef->IsExternalKey())
{
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
$aExtKeys[$sClassOfAttribute][$sAttCode] = array();
}
elseif ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
$aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef;
}
else
{
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
}
// To optimize: detect a restriction on child classes in the condition expression
// e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
$oExpression = $oAttDef->GetOQLExpression($sClass);
if (MetaModel::IsParentClass($sClassOfAttribute, $sClass))
$aRequiredFields = array();
$oExpression->GetUnresolvedFields('', $aRequiredFields);
$aTranslateFields = array();
foreach($aRequiredFields as $sSubClass => $aFields)
{
foreach($aFields as $sAttCode => $oField)
{
// The attribute is part of the standard query
//
$sAliasForAttribute = $sClassAlias;
}
else
{
// The attribute will be available from an additional outer join
// For each subclass (table) one single join is enough
//
if (!array_key_exists($sClassOfAttribute, $aFNJoinAlias))
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
if ($oAttDef->IsExternalKey())
{
$sAliasForAttribute = $oBuild->GenerateClassAlias($sClassAlias.'_fn_'.$sClassOfAttribute, $sClassOfAttribute);
$aFNJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
$aExtKeys[$sClassOfAttribute][$sAttCode] = array();
}
elseif ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
$aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef;
}
else
{
$sAliasForAttribute = $aFNJoinAlias[$sClassOfAttribute];
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
}
if (MetaModel::IsParentClass($sClassOfAttribute, $sClass))
{
// The attribute is part of the standard query
//
$sAliasForAttribute = $sClassAlias;
}
else
{
// The attribute will be available from an additional outer join
// For each subclass (table) one single join is enough
//
if (!array_key_exists($sClassOfAttribute, $aFNJoinAlias))
{
$sAliasForAttribute = $oBuild->GenerateClassAlias($sClassAlias.'_fn_'.$sClassOfAttribute, $sClassOfAttribute);
$aFNJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
}
else
{
$sAliasForAttribute = $aFNJoinAlias[$sClassOfAttribute];
}
}
$aTranslateFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
}
$aTranslateNameFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
}
}
$oNameExpression = $oNameExpression->Translate($aTranslateNameFields, false);
$oExpression = $oExpression->Translate($aTranslateFields, false);
$aTranslateNow = array();
$aTranslateNow[$sClassAlias]['friendlyname'] = $oNameExpression;
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
$aTranslateNow = array();
$aTranslateNow[$sClassAlias]['friendlyname'] = $oExpression;
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
}
}
// Add the ext fields used in the select (eventually adds an external key)
@@ -1921,7 +1926,7 @@ class DBObjectSearch extends DBSearch
//
if ($bIsOnQueriedClass && array_key_exists($sAttCode, $aValues))
{
assert ($oAttDef->IsDirectField());
assert ($oAttDef->IsBasedOnDBColumns());
foreach ($oAttDef->GetSQLValues($aValues[$sAttCode]) as $sColumn => $sValue)
{
$aUpdateValues[$sColumn] = $sValue;
@@ -2134,61 +2139,4 @@ class DBObjectSearch extends DBSearch
//MyHelpers::var_dump_html($oSelectBase->RenderSelect());
return $oSelectBase;
}
/**
* Get the friendly name for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same name expression
* Used when querying a parent class
*/
static protected function GetExtendedNameExpression($sClass)
{
// 1st step - get all of the required expressions (instantiable classes)
// and group them using their OQL representation
//
$aFNExpressions = array(); // signature => array('expression' => oExp, 'classes' => array of classes)
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sSubClass)
{
if (($sSubClass != $sClass) && MetaModel::IsAbstract($sSubClass)) continue;
$oSubClassName = MetaModel::GetNameExpression($sSubClass);
$sSignature = $oSubClassName->Render();
if (!array_key_exists($sSignature, $aFNExpressions))
{
$aFNExpressions[$sSignature] = array(
'expression' => $oSubClassName,
'classes' => array(),
);
}
$aFNExpressions[$sSignature]['classes'][] = $sSubClass;
}
// 2nd step - build the final name expression depending on the finalclass
//
if (count($aFNExpressions) == 1)
{
$aExpData = reset($aFNExpressions);
$oNameExpression = $aExpData['expression'];
}
else
{
$oNameExpression = null;
foreach ($aFNExpressions as $sSignature => $aExpData)
{
$oClassListExpr = ListExpression::FromScalars($aExpData['classes']);
$oClassExpr = new FieldExpression('finalclass', $sClass);
$oClassInList = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
if (is_null($oNameExpression))
{
$oNameExpression = $aExpData['expression'];
}
else
{
$oNameExpression = new FunctionExpression('IF', array($oClassInList, $aExpData['expression'], $oNameExpression));
}
}
}
return $oNameExpression;
}
}

View File

@@ -0,0 +1,93 @@
<?php
// Copyright (C) 2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Usage:
* require_once(...'introspection.class.inc.php');
*/
require_once('attributedef.class.inc.php');
class Introspection
{
protected $aAttributeHierarchy = array(); // class => child classes
protected $aAttributes = array();
public function __construct()
{
$this->InitAttributes();
}
protected function InitAttributes()
{
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($sPHPClass == 'AttributeDefinition' || $oRefClass->isSubclassOf('AttributeDefinition'))
{
if ($oParentClass = $oRefClass->getParentClass())
{
$sParentClass = $oParentClass->getName();
if (!array_key_exists($sParentClass, $this->aAttributeHierarchy))
{
$this->aAttributeHierarchy[$sParentClass] = array();
}
$this->aAttributeHierarchy[$sParentClass][] = $sPHPClass;
}
else
{
$sParentClass = null;
}
$this->aAttributes[$sPHPClass] = array(
'parent' => $sParentClass,
'LoadInObject' => $sPHPClass::LoadInObject(),
'LoadFromDB' => $sPHPClass::LoadFromDB(),
'IsBasedOnDBColumns' => $sPHPClass::IsBasedOnDBColumns(),
'IsBasedOnOQLExpression' => $sPHPClass::IsBasedOnOQLExpression(),
'IsExternalField' => $sPHPClass::IsExternalField(),
'IsScalar' => $sPHPClass::IsScalar(),
'IsLinkset' => $sPHPClass::IsLinkset(),
'IsHierarchicalKey' => $sPHPClass::IsHierarchicalKey(),
);
}
}
}
public function GetAttributes()
{
return $this->aAttributes;
}
public function GetAttributeHierarchy()
{
return $this->aAttributeHierarchy;
}
public function EnumAttributeCharacteristics()
{
return array(
'LoadInObject' => 'Is the value stored in the object itself?',
'LoadFromDB' => 'Is the value read from the DB?',
'IsBasedOnDBColumns' => 'Is this a value stored within one or several columns?',
'IsBasedOnOQLExpression' => 'Is this a value computed after other attributes, by the mean of an OQL expression?',
'IsExternalField' => 'Is this a value stored on a related object (external key)?',
'IsScalar' => 'Is this a value that makes sense in a SQL/OQL expression?',
'IsLinkset' => 'Is this a collection (1-N or N-N)?',
'IsHierarchicalKey' => 'Is this attribute an external key pointing to the host class?',
);
}
}

View File

@@ -4092,7 +4092,7 @@ abstract class MetaModel
}
}
}
else if ($oAttDef->IsDirectField())
else if ($oAttDef->IsBasedOnDBColumns())
{
// Check that the values fit the allowed values
//