// Second part: advanced search form: if (!empty($sFilter)) { $oFilter = DBSearch::unserialize($sFilter); } else { if (!empty($sClass)) { $oFilter = new DBObjectSearch($sClass); } } } } catch (CoreException $e) { $oFilter = new DBObjectSearch($sClass); $oP->P("<b>" . Dict::Format('UI:UniversalSearch:Error', $e->getHtmlDesc()) . "</b>"); } if ($oFilter != null) { $oSet = new CMDBObjectSet($oFilter); $oBlock = new DisplayBlock($oFilter, 'search', false); $aExtraParams = $oAppContext->GetAsHash(); $aExtraParams['open'] = true; $aExtraParams['baseClass'] = $sBaseClass; $aExtraParams['action'] = utils::GetAbsoluteUrlAppRoot() . 'pages/UniversalSearch.php'; //$aExtraParams['class'] = $sClassName; $oBlock->Display($oP, 0, $aExtraParams); // Search results $oResultBlock = new DisplayBlock($oFilter, 'list', false); $oResultBlock->Display($oP, 1); // Menu node $sFilter = $oFilter->ToOQL(); $oP->add("\n<!-- {$sFilter} -->\n"); } $oP->add("</div>\n"); $oP->output();
protected function DoExecute() { foreach (MetaModel::GetClasses() as $sClassName) { if (MetaModel::HasTable($sClassName)) { continue; } $oNobody = MetaModel::GetObject($sClassName, 123); $oBaby = new $sClassName(); $oFilter = new DBObjectSearch($sClassName); // Challenge reversibility of OQL / filter object // $sExpr1 = $oFilter->ToOQL(); $oNewFilter = DBObjectSearch::FromOQL($sExpr1); $sExpr2 = $oNewFilter->ToOQL(); if ($sExpr1 != $sExpr2) { $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>{$sExpr1}</em> != <em>{$sExpr2}</em>"); } // Use the filter (perform the query) // $oSet = new CMDBObjectSet($oFilter); $this->ReportSuccess('Found ' . $oSet->Count() . " objects of class {$sClassName}"); } return true; }
/** * Helper to link objects * * @param string sLinkAttCode * @param string sLinkedClass * @param array $aLinkList * @param DBObject oTargetObj * @param WebServiceResult oRes * * @return array List of objects that could not be found */ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes) { $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode); $sLinkClass = $oLinkAtt->GetLinkedClass(); $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote(); $aItemsFound = array(); $aItemsNotFound = array(); if (is_null($aLinkList)) { return $aItemsNotFound; } foreach ($aLinkList as $aItemData) { if (!array_key_exists('class', $aItemData)) { $oRes->LogWarning("Parameter {$sParamName}: missing 'class' specification"); continue; // skip } $sTargetClass = $aItemData['class']; if (!MetaModel::IsValidClass($sTargetClass)) { $oRes->LogError("Parameter {$sParamName}: invalid class '{$sTargetClass}'"); continue; // skip } if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) { $oRes->LogError("Parameter {$sParamName}: '{$sTargetClass}' is not a child class of '{$sLinkedClass}'"); continue; // skip } $oReconFilter = new DBObjectSearch($sTargetClass); $aCIStringDesc = array(); foreach ($aItemData['search'] as $sAttCode => $value) { if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) { $aCodes = array_keys(MetaModel::GetClassFilterDefs($sTargetClass)); $oRes->LogError("Parameter {$sParamName}: '{$sAttCode}' is not a valid filter code for class '{$sTargetClass}', expecting a value in {" . implode(', ', $aCodes) . "}"); continue 2; // skip the entire item } $aCIStringDesc[] = "{$sAttCode}: {$value}"; // The attribute is one of our reconciliation key $oReconFilter->AddCondition($sAttCode, $value, '='); } if (count($aCIStringDesc) == 1) { // take the last and unique value to describe the object $sItemDesc = $value; } else { // describe the object by the given keys $sItemDesc = $sTargetClass . '(' . implode('/', $aCIStringDesc) . ')'; } $oExtObjects = new CMDBObjectSet($oReconFilter); switch ($oExtObjects->Count()) { case 0: $oRes->LogWarning("Parameter {$sParamName}: object to link {$sLinkedClass} / {$sItemDesc} could not be found (searched: '" . $oReconFilter->ToOQL(true) . "')"); $aItemsNotFound[] = $sItemDesc; break; case 1: $aItemsFound[] = array('object' => $oExtObjects->Fetch(), 'link_values' => @$aItemData['link_values'], 'desc' => $sItemDesc); break; default: $oRes->LogWarning("Parameter {$sParamName}: Found " . $oExtObjects->Count() . " matches for item '{$sItemDesc}' (searched: '" . $oReconFilter->ToOQL(true) . "')"); $aItemsNotFound[] = $sItemDesc; } } if (count($aItemsFound) > 0) { $aLinks = array(); foreach ($aItemsFound as $aItemData) { $oLink = MetaModel::NewObject($sLinkClass); $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey()); foreach ($aItemData['link_values'] as $sKey => $value) { if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) { $oRes->LogWarning("Parameter {$sParamName}: Attaching item '" . $aItemData['desc'] . "', the attribute code '{$sKey}' is not valid ; check the class '{$sLinkClass}'"); } else { $oLink->Set($sKey, $value); } } $aLinks[] = $oLink; } $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks); $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet); } return $aItemsNotFound; }
private static function MakeQuery(&$oBuild, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQueryUNUSED = false) { // Note: query class might be different than the class of the filter // -> this occurs when we are linking our class to an external class (referenced by, or pointing to) $sClass = $oFilter->GetFirstJoinedClass(); $sClassAlias = $oFilter->GetFirstJoinedClassAlias(); $bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses()); self::DbgTrace("Entering: " . $oFilter->ToOQL() . ", " . ($bIsOnQueriedClass ? "MAIN" : "SECONDARY")); $sRootClass = self::GetRootClass($sClass); $sKeyField = self::DBGetKey($sClass); if ($bIsOnQueriedClass) { // default to the whole list of attributes + the very std id/finalclass $oBuild->m_oQBExpressions->AddSelect($sClassAlias . 'id', new FieldExpression('id', $sClassAlias)); if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad)) { $aAttList = self::ListAttributeDefs($sClass); } else { $aAttList = $aAttToLoad[$sClassAlias]; } foreach ($aAttList as $sAttCode => $oAttDef) { if (!$oAttDef->IsScalar()) { continue; } // keep because it can be used for sorting - if (!$oAttDef->LoadInObject()) continue; foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) { $oBuild->m_oQBExpressions->AddSelect($sClassAlias . $sAttCode . $sColId, new FieldExpression($sAttCode . $sColId, $sClassAlias)); } } // Transform the full text condition into additional condition expression $aFullText = $oFilter->GetCriteria_FullText(); if (count($aFullText) > 0) { $aFullTextFields = array(); foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if (!$oAttDef->IsScalar()) { continue; } if ($oAttDef->IsExternalKey()) { continue; } $aFullTextFields[] = new FieldExpression($sAttCode, $sClassAlias); } $oTextFields = new CharConcatWSExpression(' ', $aFullTextFields); foreach ($aFullText as $sFTNeedle) { $oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%{$sFTNeedle}%")); $oBuild->m_oQBExpressions->AddCondition($oNewCond); } } } //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n"; $aExpectedAtts = array(); // array of (attcode => fieldexpression) //echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n"; $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts); // Compute a clear view of required joins (from the current class) // Build the list of external keys: // -> ext keys required by an explicit join // -> ext keys mentionned in a 'pointing to' condition // -> ext keys required for an external field // -> ext keys required for a friendly name // $aExtKeys = array(); // array of sTableClass => array of (sAttCode (keys) => array of (sAttCode (fields)=> oAttDef)) // // Optimization: could be partially computed once for all (cached) ? // if ($bIsOnQueriedClass) { // Get all Ext keys for the queried class (??) foreach (self::GetKeysList($sClass) as $sKeyAttCode) { $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); } } // Get all Ext keys used by the filter foreach ($oFilter->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo) { if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo)) { $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); } } $aFNJoinAlias = array(); // array of (subclass => alias) if (array_key_exists('friendlyname', $aExpectedAtts)) { // 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) { foreach ($aFields as $sAttCode => $oField) { $oAttDef = self::GetAttributeDef($sSubClass, $sAttCode); if ($oAttDef->IsExternalKey()) { $sClassOfAttribute = self::$m_aAttribOrigins[$sSubClass][$sAttCode]; $aExtKeys[$sClassOfAttribute][$sAttCode] = array(); } elseif ($oAttDef->IsExternalField() || $oAttDef instanceof AttributeFriendlyName) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); $sClassOfAttribute = self::$m_aAttribOrigins[$sSubClass][$sKeyAttCode]; $aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef; } else { $sClassOfAttribute = self::GetAttributeOrigin($sSubClass, $sAttCode); } if (self::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]; } } $aTranslateNameFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute); } } $oNameExpression = $oNameExpression->Translate($aTranslateNameFields, false); $aTranslateNow = array(); $aTranslateNow[$sClassAlias]['friendlyname'] = $oNameExpression; $oBuild->m_oQBExpressions->Translate($aTranslateNow, false); } // Add the ext fields used in the select (eventually adds an external key) foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef->IsExternalField() || $oAttDef instanceof AttributeFriendlyName) { if (array_key_exists($sAttCode, $aExpectedAtts)) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); if ($sKeyAttCode != 'id') { // Add the external attribute $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef; } } } } // First query built upon on the leaf (ie current) class // self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()"); if (self::HasTable($sClass)) { $oSelectBase = self::MakeQuerySingleTable($oBuild, $oFilter, $sClass, $aExtKeys, $aValues); } else { $oSelectBase = null; // As the join will not filter on the expected classes, we have to specify it explicitely $sExpectedClasses = implode("', '", self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL)); $oFinalClassRestriction = Expression::FromOQL("`{$sClassAlias}`.finalclass IN ('{$sExpectedClasses}')"); $oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction); } // Then we join the queries of the eventual parent classes (compound model) foreach (self::EnumParentClasses($sClass) as $sParentClass) { if (!self::HasTable($sParentClass)) { continue; } self::DbgTrace("Parent class: {$sParentClass}... let's call MakeQuerySingleTable()"); $oSelectParentTable = self::MakeQuerySingleTable($oBuild, $oFilter, $sParentClass, $aExtKeys, $aValues); if (is_null($oSelectBase)) { $oSelectBase = $oSelectParentTable; } else { $oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, self::DBGetKey($sParentClass)); } } // Filter on objects referencing me foreach ($oFilter->GetCriteria_ReferencedBy() as $sForeignClass => $aKeysAndFilters) { foreach ($aKeysAndFilters as $sForeignKeyAttCode => $oForeignFilter) { $oForeignKeyAttDef = self::GetAttributeDef($sForeignClass, $sForeignKeyAttCode); self::DbgTrace("Referenced by foreign key: {$sForeignKeyAttCode}... let's call MakeQuery()"); //self::DbgTrace($oForeignFilter); //self::DbgTrace($oForeignFilter->ToOQL()); //self::DbgTrace($oSelectForeign); //self::DbgTrace($oSelectForeign->RenderSelect(array())); $sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias(); $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias)); $oSelectForeign = self::MakeQuery($oBuild, $oForeignFilter, $aAttToLoad); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField(); $sForeignKeyTable = $oJoinExpr->GetParent(); $sForeignKeyColumn = $oJoinExpr->GetName(); $oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable); } } // Filter on related objects // foreach ($oFilter->GetCriteria_RelatedTo() as $aCritInfo) { $oSubFilter = $aCritInfo['flt']; $sRelCode = $aCritInfo['relcode']; $iMaxDepth = $aCritInfo['maxdepth']; // Get the starting point objects $oStartSet = new CMDBObjectSet($oSubFilter); // Get the objects related to those objects... recursively... $aRelatedObjs = $oStartSet->GetRelatedObjects($sRelCode, $iMaxDepth); $aRestriction = array_key_exists($sRootClass, $aRelatedObjs) ? $aRelatedObjs[$sRootClass] : array(); // #@# todo - related objects and expressions... // Create condition if (count($aRestriction) > 0) { $oSelectBase->AddCondition($sKeyField . ' IN (' . implode(', ', CMDBSource::Quote(array_keys($aRestriction), true)) . ')'); } else { // Quick N'dirty -> generate an empty set $oSelectBase->AddCondition('false'); } } // Additional JOINS for Friendly names // foreach ($aFNJoinAlias as $sSubClass => $sSubClassAlias) { $oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias); $oSelectFN = self::MakeQuerySingleTable($oBuild, $oSubClassFilter, $sSubClass, $aExtKeys, array()); $oSelectBase->AddLeftJoin($oSelectFN, $sKeyField, self::DBGetKey($sSubClass)); } // That's all... cross fingers and we'll get some working query //MyHelpers::var_dump_html($oSelectBase, true); //MyHelpers::var_dump_html($oSelectBase->RenderSelect(), true); if (self::$m_bDebugQuery) { $oSelectBase->DisplayHtml(); } return $oSelectBase; }