From d4816ddc51727df287cd1c26b68c3e62b9c5691f Mon Sep 17 00:00:00 2001
From: Romain Quetiez
Date: Wed, 8 Feb 2012 15:16:16 +0000
Subject: [PATCH] Implemented the capability to modify queries by the mean of a
plugin (reintegrated from branch 1.2, release candidate)
SVN:trunk[1849]
---
application/applicationcontext.class.inc.php | 53 ++++++
core/cmdbobject.class.inc.php | 17 ++
core/dbobject.class.php | 5 +
core/dbobjectsearch.class.php | 19 +++
core/expression.class.inc.php | 4 +-
core/metamodel.class.php | 168 ++++++++++++-------
core/querybuildercontext.class.inc.php | 74 ++++++++
core/querymodifier.class.inc.php | 33 ++++
core/sqlquery.class.inc.php | 54 ++++--
pages/csvimport.php | 3 +
10 files changed, 354 insertions(+), 76 deletions(-)
create mode 100644 core/querybuildercontext.class.inc.php
create mode 100644 core/querymodifier.class.inc.php
diff --git a/application/applicationcontext.class.inc.php b/application/applicationcontext.class.inc.php
index 374b6ceb0..0d5f27f4d 100644
--- a/application/applicationcontext.class.inc.php
+++ b/application/applicationcontext.class.inc.php
@@ -303,5 +303,58 @@ class ApplicationContext
return '';
}
}
+
+ protected static $m_aPluginProperties = null;
+
+ /**
+ * Load plugin properties for the current session
+ * @return void
+ */
+ protected static function LoadPluginProperties()
+ {
+ if (isset($_SESSION['PluginProperties']))
+ {
+ self::$m_aPluginProperties = $_SESSION['PluginProperties'];
+ }
+ else
+ {
+ self::$m_aPluginProperties = array();
+ }
+ }
+
+ /**
+ * Set plugin properties
+ * @param sPluginClass string Class implementing any plugin interface
+ * @param sProperty string Name of the property
+ * @param value scalar Value (numeric or string)
+ * @return void
+ */
+ public static function SetPluginProperty($sPluginClass, $sProperty, $value)
+ {
+ if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
+
+ self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
+ $_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
+ }
+
+ /**
+ * Get plugin properties
+ * @param sPluginClass string Class implementing any plugin interface
+ * @return array of sProperty=>value pairs
+ */
+ public static function GetPluginProperties($sPluginClass)
+ {
+ if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
+
+ if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
+ {
+ return self::$m_aPluginProperties[$sPluginClass];
+ }
+ else
+ {
+ return array();
+ }
+ }
+
}
?>
diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php
index a31b8ede9..5c0aa9051 100644
--- a/core/cmdbobject.class.inc.php
+++ b/core/cmdbobject.class.inc.php
@@ -93,6 +93,23 @@ abstract class CMDBObject extends DBObject
protected static $m_oCurrChange = null;
+ public static function SetCurrentChange(CMDBChange $oChange)
+ {
+ self::$m_oCurrChange = $oChange;
+ }
+
+ //
+ // Todo: simplify the APIs and do not pass the current change as an argument anymore
+ // SetCurrentChange to be invoked in very few cases (UI.php, CSV import, Data synchro)
+ // GetCurrentChange to be called ONCE (!) by CMDBChangeOp::OnInsert ($this->Set('change', ..GetCurrentChange())
+ // GetCurrentChange to create a default change if not already done in the current context
+ //
+ public static function GetCurrentChange()
+ {
+ return self::$m_oCurrChange;
+ }
+
+
private function RecordObjCreation(CMDBChange $oChange)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
diff --git a/core/dbobject.class.php b/core/dbobject.class.php
index a87796a94..cc99af76b 100644
--- a/core/dbobject.class.php
+++ b/core/dbobject.class.php
@@ -1302,6 +1302,11 @@ abstract class DBObject
return $this->DBInsert();
}
+ public function DBInsertTrackedNoReload(CMDBChange $oVoid)
+ {
+ return $this->DBInsertNoReload();
+ }
+
// Creates a copy of the current object into the database
// Returns the id of the newly created object
public function DBClone($iNewKey = null)
diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php
index 0d6c54247..a8add04e2 100644
--- a/core/dbobjectsearch.class.php
+++ b/core/dbobjectsearch.class.php
@@ -66,6 +66,8 @@ class DBObjectSearch
$this->m_aRelatedTo = array();
$this->m_bDataFiltered = false;
$this->m_aParentConditions = array();
+
+ $this->m_aModifierProperties = array();
}
public function AllowAllData() {$this->m_bAllowAllData = true;}
@@ -126,6 +128,23 @@ class DBObjectSearch
}
+ public function SetModifierProperty($sPluginClass, $sProperty, $value)
+ {
+ $this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
+ }
+
+ public function GetModifierProperties($sPluginClass)
+ {
+ if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
+ {
+ return $this->m_aModifierProperties[$sPluginClass];
+ }
+ else
+ {
+ return array();
+ }
+ }
+
public function IsAny()
{
// #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false;
diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php
index ad2590ca9..75f6184fa 100644
--- a/core/expression.class.inc.php
+++ b/core/expression.class.inc.php
@@ -953,10 +953,10 @@ class QueryBuilderExpressions
protected $m_aSelectExpr;
protected $m_aJoinFields;
- public function __construct($aSelect, $oCondition)
+ public function __construct($oCondition)
{
$this->m_oConditionExpr = $oCondition;
- $this->m_aSelectExpr = $aSelect;
+ $this->m_aSelectExpr = array();
$this->m_aJoinFields = array();
}
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index 6b7c97bf1..f66eb8b40 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -15,6 +15,8 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
require_once(APPROOT.'core/modulehandler.class.inc.php');
+require_once(APPROOT.'core/querybuildercontext.class.inc.php');
+require_once(APPROOT.'core/querymodifier.class.inc.php');
/**
* Metamodel
@@ -25,8 +27,6 @@ require_once(APPROOT.'core/modulehandler.class.inc.php');
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
-
-
// #@# todo: change into class const (see Doctrine)
// Doctrine example
// class toto
@@ -1404,7 +1404,7 @@ abstract class MetaModel
// Build the list of available extensions
//
- $aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension');
+ $aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
@@ -1924,12 +1924,26 @@ abstract class MetaModel
}
}
+ // Compute query modifiers properties (can be set in the search itself, by the context, etc.)
+ //
+ $aModifierProperties = self::MakeModifierProperties($oFilter);
+
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
{
// Need to identify the query
$sOqlQuery = $oFilter->ToOql();
- $sRawId = $sOqlQuery;
+ if (count($aModifierProperties))
+ {
+ array_multisort($aModifierProperties);
+ $sModifierProperties = json_encode($aModifierProperties);
+ }
+ else
+ {
+ $sModifierProperties = '';
+ }
+
+ $sRawId = $sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
foreach($aAttToLoad as $sAlias => $aAttributes)
@@ -2024,12 +2038,10 @@ abstract class MetaModel
if (!isset($oSelect))
{
- $aClassAliases = array();
- $aTableAliases = array();
- $oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
+ $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
$oKPI = new ExecutionKPI();
- $oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $aAttToLoad, array(), true /* main query */);
+ $oSelect = self::MakeQuery($oBuild, $oFilter, $aAttToLoad, array(), true /* main query */);
$oSelect->SetSourceOQL($sOqlQuery);
$oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery);
@@ -2138,12 +2150,33 @@ abstract class MetaModel
}
}
+ protected static function MakeModifierProperties($oFilter)
+ {
+ // Compute query modifiers properties (can be set in the search itself, by the context, etc.)
+ //
+ $aModifierProperties = array();
+ foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
+ {
+ // Lowest precedence: the application context
+ $aPluginProps = ApplicationContext::GetPluginProperties($sPluginClass);
+ // Highest precedence: programmatically specified (or OQL)
+ foreach($oFilter->GetModifierProperties($sPluginClass) as $sProp => $value)
+ {
+ $aPluginProps[$sProp] = $value;
+ }
+ if (count($aPluginProps) > 0)
+ {
+ $aModifierProperties[$sPluginClass] = $aPluginProps;
+ }
+ }
+ return $aModifierProperties;
+ }
+
public static function MakeDeleteQuery(DBObjectSearch $oFilter, $aArgs = array())
{
- $aClassAliases = array();
- $aTableAliases = array();
- $oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
- $oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, null, array(), true /* main query */);
+ $aModifierProperties = self::MakeModifierProperties($oFilter);
+ $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
+ $oSelect = self::MakeQuery($oBuild, $oFilter, null, array(), true /* main query */);
$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
return $oSelect->RenderDelete($aScalarArgs);
}
@@ -2151,26 +2184,21 @@ abstract class MetaModel
public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues, $aArgs = array())
{
// $aValues is an array of $sAttCode => $value
- $aClassAliases = array();
- $aTableAliases = array();
- $oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
- $oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, null, $aValues, true /* main query */);
+ $aModifierProperties = self::MakeModifierProperties($oFilter);
+ $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
+ $oSelect = self::MakeQuery($oBuild, $oFilter, null, $aValues, true /* main query */);
$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
return $oSelect->RenderUpdate($aScalarArgs);
}
- private static function MakeQuery($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQuery = false)
+ private static function MakeQuery(&$oBuild, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQuery = 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, $aSelectedClasses);
- if ($bIsOnQueriedClass)
- {
- $aClassAliases = array_merge($aClassAliases, $oFilter->GetJoinedClasses());
- }
+ $bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
self::DbgTrace("Entering: ".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
@@ -2180,7 +2208,7 @@ abstract class MetaModel
if ($bIsOnQueriedClass)
{
// default to the whole list of attributes + the very std id/finalclass
- $oQBExpr->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
+ $oBuild->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad))
{
@@ -2196,7 +2224,7 @@ abstract class MetaModel
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{
- $oQBExpr->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
+ $oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
}
}
@@ -2216,14 +2244,14 @@ abstract class MetaModel
foreach($aFullText as $sFTNeedle)
{
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
- $oQBExpr->AddCondition($oNewCond);
+ $oBuild->m_oQBExpressions->AddCondition($oNewCond);
}
}
}
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
$aExpectedAtts = array(); // array of (attcode => fieldexpression)
//echo "".__LINE__.": GetUnresolvedFields($sClassAlias, ...)
\n";
- $oQBExpr->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
+ $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
// Compute a clear view of required joins (from the current class)
// Build the list of external keys:
@@ -2260,9 +2288,9 @@ abstract class MetaModel
{
$aTranslateNow = array();
$aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias);
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
- $oQBExpr->Translate($aTranslateNow, false);
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
+ $oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
$aNameSpec = self::GetNameSpec($sClass);
foreach($aNameSpec[1] as $i => $sAttCode)
@@ -2304,7 +2332,7 @@ abstract class MetaModel
self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()");
if (self::HasTable($sClass))
{
- $oSelectBase = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sClass, $aExtKeys, $aValues);
+ $oSelectBase = self::MakeQuerySingleTable($oBuild, $oFilter, $sClass, $aExtKeys, $aValues);
}
else
{
@@ -2313,7 +2341,7 @@ abstract class MetaModel
// 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')");
- $oQBExpr->AddCondition($oFinalClassRestriction);
+ $oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
// Then we join the queries of the eventual parent classes (compound model)
@@ -2322,7 +2350,7 @@ abstract class MetaModel
if (!self::HasTable($sParentClass)) continue;
//echo "Parent class: $sParentClass... let's call MakeQuerySingleTable()
";
self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()");
- $oSelectParentTable = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sParentClass, $aExtKeys, $aValues);
+ $oSelectParentTable = self::MakeQuerySingleTable($oBuild, $oFilter, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
@@ -2347,11 +2375,11 @@ abstract class MetaModel
//self::DbgTrace($oSelectForeign->RenderSelect(array()));
$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
- $oQBExpr->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias));
+ $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias));
- $oSelectForeign = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oForeignFilter, $aAttToLoad);
+ $oSelectForeign = self::MakeQuery($oBuild, $oForeignFilter, $aAttToLoad);
- $oJoinExpr = $oQBExpr->PopJoinField();
+ $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sForeignKeyTable = $oJoinExpr->GetParent();
$sForeignKeyColumn = $oJoinExpr->GetName();
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
@@ -2390,8 +2418,8 @@ abstract class MetaModel
//
if ($bIsMainQuery)
{
- $oSelectBase->SetCondition($oQBExpr->GetCondition());
- $oSelectBase->SetSelect($oQBExpr->GetSelect());
+ $oSelectBase->SetCondition($oBuild->m_oQBExpressions->GetCondition());
+ $oSelectBase->SetSelect($oBuild->m_oQBExpressions->GetSelect());
}
// That's all... cross fingers and we'll get some working query
@@ -2402,7 +2430,7 @@ abstract class MetaModel
return $oSelectBase;
}
- protected static function MakeQuerySingleTable($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, $oFilter, $sTableClass, $aExtKeys, $aValues)
+ protected static function MakeQuerySingleTable(&$oBuild, $oFilter, $sTableClass, $aExtKeys, $aValues)
{
// $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields))
//echo "MAKEQUERY($sTableClass)-liste des clefs externes($sTableClass): ".print_r($aExtKeys, true)."
\n";
@@ -2416,13 +2444,13 @@ abstract class MetaModel
$sTargetClass = $oFilter->GetFirstJoinedClass();
$sTargetAlias = $oFilter->GetFirstJoinedClassAlias();
$sTable = self::DBGetTable($sTableClass);
- $sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTargetAlias.'_'.$sTable, $sTable);
+ $sTableAlias = $oBuild->GenerateTableAlias($sTargetAlias.'_'.$sTable, $sTable);
$aTranslation = array();
$aExpectedAtts = array();
- $oQBExpr->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
+ $oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
- $bIsOnQueriedClass = array_key_exists($sTargetAlias, $aSelectedClasses);
+ $bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
@@ -2471,6 +2499,18 @@ abstract class MetaModel
$aUpdateValues[$sColumn] = $sValue;
}
}
+ }
+
+ // 2 - The SQL query, for this table only
+ //
+ $oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
+
+ // 3 - Resolve expected expressions (translation table: alias.attcode => table.column)
+ //
+ foreach(self::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
+ {
+ // Skip this attribute if not defined in this table
+ if (self::$m_aAttribOrigins[$sTargetClass][$sAttCode] != $sTableClass) continue;
// Select...
//
@@ -2487,16 +2527,17 @@ abstract class MetaModel
{
if (array_key_exists($sAttCode, $aExpectedAtts))
{
- $aTranslation[$sTargetAlias][$sAttCode.$sColId] = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
+ $oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
+ foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
+ {
+ $oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sTargetClass, $sAttCode, $sColId, $oFieldSQLExp, $oSelectBase);
+ }
+ $aTranslation[$sTargetAlias][$sAttCode.$sColId] = $oFieldSQLExp;
}
}
}
}
- // 3 - The whole stuff, for this table only
- //
- $oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
-
//echo "MAKEQUERY- Classe $sTableClass
\n";
// 4 - The external keys -> joins...
//
@@ -2516,7 +2557,7 @@ abstract class MetaModel
// The join was not explicitely defined in the filter,
// we need to do it now
$sKeyClass = $oKeyAttDef->GetTargetClass();
- $sKeyClassAlias = self::GenerateUniqueAlias($aClassAliases, $sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
+ $sKeyClassAlias = $oBuild->GenerateClassAlias($sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
$oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias);
$aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter;
@@ -2573,18 +2614,18 @@ abstract class MetaModel
}
// Translate prior to recursing
//
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."\n".print_r($aTranslateNow, true)."
\n";
- $oQBExpr->Translate($aTranslateNow, false);
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n".print_r($aTranslateNow, true)."
\n";
+ $oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
//echo "External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()/p>\n";
self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()");
- $oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
+ $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
-//echo "
Recursive MakeQuery ".__LINE__.":
\n".print_r($aSelectedClasses, true)."
\n";
- $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter);
+//echo "Recursive MakeQuery ".__LINE__.":
\n".print_r($oBuild->GetRootFilter()->GetSelectedClasses(), true)."
\n";
+ $oSelectExtKey = self::MakeQuery($oBuild, $oExtFilter);
- $oJoinExpr = $oQBExpr->PopJoinField();
+ $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName();
@@ -2604,9 +2645,9 @@ abstract class MetaModel
}
elseif(self::$m_aAttribOrigins[$sKeyClass][$sKeyAttCode] == $sTableClass)
{
- $oQBExpr->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias));
- $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter);
- $oJoinExpr = $oQBExpr->PopJoinField();
+ $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias));
+ $oSelectExtKey = self::MakeQuery($oBuild, $oExtFilter);
+ $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
//echo "MAKEQUERY-PopJoinField pour $sKeyAttCode, $sKeyClassAlias: ".print_r($oJoinExpr, true)."
\n";
$sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName();
@@ -2625,9 +2666,9 @@ abstract class MetaModel
// Translate the selected columns
//
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
- $oQBExpr->Translate($aTranslation, false);
-//echo "oQBExpr ".__LINE__.":
\n".print_r($oQBExpr, true)."
\n";
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
+ $oBuild->m_oQBExpressions->Translate($aTranslation, false);
+//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."
\n";
//MyHelpers::var_dump_html($oSelectBase->RenderSelect());
return $oSelectBase;
@@ -4660,7 +4701,7 @@ abstract class MetaModel
}
/**
- * Returns an array of classes implementing the given interface
+ * Returns an array of classes=>instance implementing the given interface
*/
public static function EnumPlugins($sInterface)
{
@@ -4743,5 +4784,4 @@ MetaModel::RegisterZList("preview", array("description"=>"All attributes visible
MetaModel::RegisterZList("standard_search", array("description"=>"List of criteria for the standard search", "type"=>"filters"));
MetaModel::RegisterZList("advanced_search", array("description"=>"List of criteria for the advanced search", "type"=>"filters"));
-
-?>
+?>
\ No newline at end of file
diff --git a/core/querybuildercontext.class.inc.php b/core/querybuildercontext.class.inc.php
new file mode 100644
index 000000000..8c6e88544
--- /dev/null
+++ b/core/querybuildercontext.class.inc.php
@@ -0,0 +1,74 @@
+ MakeQuery/MakeQuerySingleTable
+ *
+ * @author Erwan Taloc
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+class QueryBuilderContext
+{
+ protected $m_oRootFilter;
+ protected $m_aClassAliases;
+ protected $m_aTableAliases;
+ protected $m_aModifierProperties;
+
+ public $m_oQBExpressions;
+
+ public function __construct($oFilter, $aModifierProperties)
+ {
+ $this->m_oRootFilter = $oFilter;
+ $this->m_oQBExpressions = new QueryBuilderExpressions($oFilter->GetCriteria());
+
+ $this->m_aClassAliases = $oFilter->GetJoinedClasses();
+ $this->m_aTableAliases = array();
+
+ $this->m_aModifierProperties = $aModifierProperties;
+ }
+
+ public function GetRootFilter()
+ {
+ return $this->m_oRootFilter;
+ }
+
+ public function GenerateTableAlias($sNewName, $sRealName)
+ {
+ return MetaModel::GenerateUniqueAlias($this->m_aTableAliases, $sNewName, $sRealName);
+ }
+
+ public function GenerateClassAlias($sNewName, $sRealName)
+ {
+ return MetaModel::GenerateUniqueAlias($this->m_aClassAliases, $sNewName, $sRealName);
+ }
+
+ public function GetModifierProperties($sPluginClass)
+ {
+ if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
+ {
+ return $this->m_aModifierProperties[$sPluginClass];
+ }
+ else
+ {
+ return array();
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/core/querymodifier.class.inc.php b/core/querymodifier.class.inc.php
new file mode 100644
index 000000000..8fdfcf6cc
--- /dev/null
+++ b/core/querymodifier.class.inc.php
@@ -0,0 +1,33 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+interface iQueryModifier
+{
+ public function __construct();
+
+ public function GetFieldExpression(QueryBuilderContext &$oBuild, $sClass, $sAttCode, $sColId, Expression $oFieldSQLExp, SQLQuery &$oSelect);
+}
+?>
diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php
index e39c89b81..582206573 100644
--- a/core/sqlquery.class.inc.php
+++ b/core/sqlquery.class.inc.php
@@ -68,6 +68,11 @@ class SQLQuery
$this->m_oSelectedIdField = $oSelectedIdField;
}
+ public function GetTableAlias()
+ {
+ return $this->m_sTableAlias;
+ }
+
public function SetSourceOQL($sOQL)
{
$this->m_SourceOQL = $sOQL;
@@ -101,11 +106,20 @@ class SQLQuery
{
$sJoinType = $aJoinInfo["jointype"];
$oSQLQuery = $aJoinInfo["select"];
- $sLeftField = $aJoinInfo["leftfield"];
- $sRightField = $aJoinInfo["rightfield"];
- $sRightTableAlias = $aJoinInfo["righttablealias"];
+ if (isset($aJoinInfo["on_expression"]))
+ {
+ $sOnCondition = $aJoinInfo["on_expression"]->Render();
- echo "Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."\n";
+ echo "Join '$sJoinType', ON ($sOnCondition)".$oSQLQuery->DisplayHtml()."\n";
+ }
+ else
+ {
+ $sLeftField = $aJoinInfo["leftfield"];
+ $sRightField = $aJoinInfo["rightfield"];
+ $sRightTableAlias = $aJoinInfo["righttablealias"];
+
+ echo "Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."\n";
+ }
}
echo "";
}
@@ -196,6 +210,24 @@ class SQLQuery
{
return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField);
}
+
+ public function AddInnerJoinEx(SQLQuery $oSQLQuery, Expression $oOnExpression)
+ {
+ $this->m_aJoinSelects[] = array(
+ "jointype" => 'inner',
+ "select" => $oSQLQuery,
+ "on_expression" => $oOnExpression
+ );
+ }
+
+ public function AddLeftJoinEx(SQLQuery $oSQLQuery, Expression $oOnExpression)
+ {
+ $this->m_aJoinSelects[] = array(
+ "jointype" => 'left',
+ "select" => $oSQLQuery,
+ "on_expression" => $oOnExpression
+ );
+ }
// Interface, build the SQL query
public function RenderDelete($aArgs = array())
@@ -412,8 +444,14 @@ class SQLQuery
break;
case "inner":
case "left":
- // table or tablealias ???
- $sJoinCond = "`$sCallerAlias`.`{$aJoinData['leftfield']}` = `$sRightTableAlias`.`{$aJoinData['rightfield']}`";
+ if (isset($aJoinData["on_expression"]))
+ {
+ $sJoinCond = $aJoinData["on_expression"]->Render();
+ }
+ else
+ {
+ $sJoinCond = "`$sCallerAlias`.`{$aJoinData['leftfield']}` = `$sRightTableAlias`.`{$aJoinData['rightfield']}`";
+ }
$aFrom[$this->m_sTableAlias] = array("jointype"=>$aJoinData['jointype'], "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond");
break;
case "inner_tree":
@@ -488,10 +526,6 @@ class SQLQuery
foreach ($this->m_aJoinSelects as $aJoinData)
{
$oRightSelect = $aJoinData["select"];
-// $sJoinType = $aJoinData["jointype"];
-// $sLeftField = $aJoinData["leftfield"];
-// $sRightField = $aJoinData["rightfield"];
-// $sRightTableAlias = $aJoinData["righttablealias"];
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, $this->m_sTableAlias, $aJoinData);
}
diff --git a/pages/csvimport.php b/pages/csvimport.php
index bd64ee5b2..3264fb168 100644
--- a/pages/csvimport.php
+++ b/pages/csvimport.php
@@ -304,6 +304,9 @@ try
$sUserString .= ' (CSV)';
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
+
+ // Todo - simplify that when reworking the change tracking
+ CMDBObject::SetCurrentChange($oMyChange);
}
$oBulk = new BulkChange(