Ejemplo n.º 1
0
 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;
 }