public function GetRelationQueryData() { foreach (MetaModel::EnumRelationQueries($this->GetHostClass(), $this->Get('relation_code'), false) as $sDummy => $aQueryInfo) { if ($aQueryInfo['sFromClass'] == $this->Get('from_class')) { if ($aQueryInfo['sNeighbour'] == $this->Get('neighbour_id')) { return $aQueryInfo; } } } }
/** * Recursively find related objects, and add them into the graph * * @param string $sRelCode The code of the relation to use for the computation * @param boolean $bDown The direction: downstream or upstream * @param array $oObjectNode The node from which to compute the neighbours * @param int $iMaxDepth * @param boolean $bEnableReduncancy * * @return void */ protected function AddRelatedObjects($sRelCode, $bDown, $oObjectNode, $iMaxDepth, $bEnableRedundancy) { if ($iMaxDepth > 0) { if ($oObjectNode instanceof RelationRedundancyNode) { // Note: this happens when recursing on an existing part of the graph // Skip that redundancy node $aRelatedEdges = $bDown ? $oObjectNode->GetOutgoingEdges() : $oObjectNode->GetIncomingEdges(); foreach ($aRelatedEdges as $oRelatedEdge) { $oRelatedNode = $bDown ? $oRelatedEdge->GetSinkNode() : $oRelatedEdge->GetSourceNode(); // Recurse (same depth) $this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth, $bEnableRedundancy); } } elseif ($oObjectNode->GetProperty('developped', false)) { // No need to execute the queries again... just dig into the nodes down/up to iMaxDepth // $aRelatedEdges = $bDown ? $oObjectNode->GetOutgoingEdges() : $oObjectNode->GetIncomingEdges(); foreach ($aRelatedEdges as $oRelatedEdge) { $oRelatedNode = $bDown ? $oRelatedEdge->GetSinkNode() : $oRelatedEdge->GetSourceNode(); // Recurse (decrement the depth) $this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth - 1, $bEnableRedundancy); } } else { $oObjectNode->SetProperty('developped', true); $oObject = $oObjectNode->GetProperty('object'); foreach (MetaModel::EnumRelationQueries(get_class($oObject), $sRelCode, $bDown) as $sDummy => $aQueryInfo) { $sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp']; try { $oFlt = DBObjectSearch::FromOQL($sQuery); $oObjSet = new DBObjectSet($oFlt, array(), $oObject->ToArgsForQuery()); $oRelatedObj = $oObjSet->Fetch(); } catch (Exception $e) { $sDirection = $bDown ? 'downstream' : 'upstream'; throw new Exception("Wrong query ({$sDirection}) for the relation {$sRelCode}/{$aQueryInfo['sDefinedInClass']}/{$aQueryInfo['sNeighbour']}: " . $e->getMessage()); } if ($oRelatedObj) { do { $sObjectRef = RelationObjectNode::MakeId($oRelatedObj); $oRelatedNode = $this->GetNode($sObjectRef); if (is_null($oRelatedNode)) { $oRelatedNode = new RelationObjectNode($this, $oRelatedObj); } $oSourceNode = $bDown ? $oObjectNode : $oRelatedNode; $oSinkNode = $bDown ? $oRelatedNode : $oObjectNode; if ($bEnableRedundancy) { $oRedundancyNode = $this->ComputeRedundancy($sRelCode, $aQueryInfo, $oSourceNode, $oSinkNode); } else { $oRedundancyNode = null; } if (!$oRedundancyNode) { // Direct link (otherwise handled by ComputeRedundancy) $oEdge = new RelationEdge($this, $oSourceNode, $oSinkNode); } // Recurse $this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth - 1, $bEnableRedundancy); } while ($oRelatedObj = $oObjSet->Fetch()); } } } } }
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) { foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo) { MetaModel::DbgTrace("object=" . $this->GetKey() . ", depth={$iMaxDepth}, rel=" . $aQueryInfo["sQuery"]); $sQuery = $aQueryInfo["sQuery"]; $bPropagate = $aQueryInfo["bPropagate"]; $iDistance = $aQueryInfo["iDistance"]; $iDepth = $bPropagate ? $iMaxDepth - 1 : 0; $oFlt = DBObjectSearch::FromOQL($sQuery); $oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery()); while ($oObj = $oObjSet->Fetch()) { $sRootClass = MetaModel::GetRootClass(get_class($oObj)); $sObjKey = $oObj->GetKey(); if (array_key_exists($sRootClass, $aResults)) { if (array_key_exists($sObjKey, $aResults[$sRootClass])) { continue; // already visited, skip } } $aResults[$sRootClass][$sObjKey] = $oObj; if ($iDepth > 0) { $oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults); } } } return $aResults; }
/** * Display the details of a given relation (e.g. "impacts") */ function DisplayRelationDetails($oPage, $sRelCode, $sContext) { $sDesc = MetaModel::GetRelationDescription($sRelCode); $sVerbDown = MetaModel::GetRelationVerbDown($sRelCode); $sVerbUp = MetaModel::GetRelationVerbUp($sRelCode); $oPage->add("<h1>" . Dict::Format('UI:Schema:Relation_Code_Description', $sRelCode, $sDesc) . "</h1>"); $oPage->p(Dict::Format('UI:Schema:RelationDown_Description', $sVerbDown)); $oPage->p(Dict::Format('UI:Schema:RelationUp_Description', $sVerbUp)); $oPage->add("<ul id=\"RelationshipDetails\" class=\"treeview\">\n"); foreach (MetaModel::GetClasses() as $sClass) { $aRelQueries = MetaModel::EnumRelationQueries($sClass, $sRelCode); if (count($aRelQueries) > 0) { $oPage->add("<li>class " . MakeClassHLink($sClass, $sContext) . "\n"); $oPage->add("<ul>\n"); foreach ($aRelQueries as $sRelKey => $aQuery) { $sQuery = $aQuery['sQuery']; $iDistance = $aQuery['iDistance']; if ($aQuery['bPropagate']) { $oPage->add("<li>" . Dict::Format('UI:Schema:RelationPropagates', $sRelKey, $iDistance, $sQuery) . "</li>\n"); } else { $oPage->add("<li>" . Dict::Format('UI:Schema:RelationDoesNotPropagate', $sRelKey, $iDistance, $sQuery) . "</li>\n"); } } $oPage->add("</ul>\n"); $oPage->add("</li>\n"); } } $oPage->add_ready_script('$("#RelationshipDetails").treeview();'); }
/** * Display the details of a given relation (e.g. "impacts") */ function DisplayRelationDetails($oPage, $sRelCode, $sContext) { $sDesc = MetaModel::GetRelationDescription($sRelCode); $sLabel = MetaModel::GetRelationLabel($sRelCode); $oPage->add("<h1>" . Dict::Format('UI:Schema:Relation_Code_Description', $sRelCode, $sDesc) . "</h1>"); $oPage->p(Dict::Format('UI:Schema:RelationUp_Description', $sLabel)); $oPage->add("<ul id=\"RelationshipDetails\" class=\"treeview\">\n"); foreach (MetaModel::GetClasses() as $sClass) { $aRelQueries = MetaModel::EnumRelationQueries($sClass, $sRelCode); if (count($aRelQueries) > 0) { $oPage->add("<li>class " . MakeClassHLink($sClass, $sContext) . "\n"); $oPage->add("<ul>\n"); foreach ($aRelQueries as $sRelKey => $aQuery) { $sQueryDown = isset($aQuery['sQueryDown']) ? $aQuery['sQueryDown'] : ''; $sQueryUp = isset($aQuery['sQueryUp']) ? $aQuery['sQueryUp'] : ''; $sAttribute = isset($aQuery['sAttribute']) ? $aQuery['sAttribute'] : ''; /* if ($aQuery['bPropagate']) { $oPage->add("<li>".Dict::Format('UI:Schema:RelationPropagates', $sRelKey, $iDistance, $sQuery)."</li>\n"); } else { $oPage->add("<li>".Dict::Format('UI:Schema:RelationDoesNotPropagate', $sRelKey, $iDistance, $sQuery)."</li>\n"); } */ $sLabel = strlen($sQueryDown) > 0 ? $sQueryDown : $sAttribute; if ($aQuery['_legacy_']) { $sLabel .= ' (<b>Old style specification</b>: it is recommended to upgrade to XML)'; } $oPage->add("<li>" . $sLabel . "</li>\n"); } $oPage->add("</ul>\n"); $oPage->add("</li>\n"); } } $oPage->add_ready_script('$("#RelationshipDetails").treeview();'); }
/** * Will be deprecated soon - use GetRelatedObjectsDown/Up instead to take redundancy into account */ public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) { // Temporary patch: until the impact analysis GUI gets rewritten, // let's consider that "depends on" is equivalent to "impacts/up" // The current patch has been implemented in DBObject and MetaModel $sHackedRelCode = $sRelCode; $bDown = true; if ($sRelCode == 'depends on') { $sHackedRelCode = 'impacts'; $bDown = false; } foreach (MetaModel::EnumRelationQueries(get_class($this), $sHackedRelCode, $bDown) as $sDummy => $aQueryInfo) { $sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp']; //$bPropagate = $aQueryInfo["bPropagate"]; //$iDepth = $bPropagate ? $iMaxDepth - 1 : 0; $iDepth = $iMaxDepth - 1; // Note: the loop over the result set has been written in an unusual way for error reporting purposes // In the case of a wrong query parameter name, the error occurs on the first call to Fetch, // thus we need to have this first call into the try/catch, but // we do NOT want to nest the try/catch for the error message to be clear try { $oFlt = DBObjectSearch::FromOQL($sQuery); $oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery()); $oObj = $oObjSet->Fetch(); } catch (Exception $e) { $sClassOfDefinition = $aQueryInfo['_legacy_'] ? get_class($this) . '(or a parent)::GetRelationQueries()' : $aQueryInfo['sDefinedInClass']; throw new Exception("Wrong query for the relation {$sRelCode}/{$sClassOfDefinition}/{$aQueryInfo['sNeighbour']}: " . $e->getMessage()); } if ($oObj) { do { $sRootClass = MetaModel::GetRootClass(get_class($oObj)); $sObjKey = $oObj->GetKey(); if (array_key_exists($sRootClass, $aResults)) { if (array_key_exists($sObjKey, $aResults[$sRootClass])) { continue; // already visited, skip } } $aResults[$sRootClass][$sObjKey] = $oObj; if ($iDepth > 0) { $oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults); } } while ($oObj = $oObjSet->Fetch()); } } return $aResults; }
function test_relations() { echo "<h4>Test relations</h4>"; //self::DumpVariable(MetaModel::EnumRelationQueries("cmdbObjectHomeMade", "Potes")); self::DumpVariable(MetaModel::EnumRelationQueries("cmdbContact", "Potes")); $iMaxDepth = 9; echo "Max depth = {$iMaxDepth}</br>\n"; $oObj = MetaModel::GetObject("cmdbContact", 18); $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth); echo $oObj->Get('name') . " has some 'Potes'...</br>\n"; foreach ($aRels as $sClass => $aObjs) { echo "{$sClass}, count = " . count($aObjs) . " => " . implode(', ', array_keys($aObjs)) . "</br>\n"; $oObjectSet = CMDBObjectSet::FromArray($sClass, $aObjs); $this->show_list($oObjectSet); } echo "<h4>Test relations - same results, by the mean of a OQL</h4>"; $this->search_and_show_list_from_oql("cmdbContact: RELATED (Potes, {$iMaxDepth}) TO (cmdbContact: pkey = 18)"); }