mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
291 lines
8.6 KiB
PHP
291 lines
8.6 KiB
PHP
<?php
|
|
/**
|
|
* Class SQLObjectQueryBuilder
|
|
*
|
|
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
* @license http://opensource.org/licenses/AGPL-3.0
|
|
*/
|
|
|
|
|
|
class SQLObjectQueryBuilder
|
|
{
|
|
/** @var \DBObjectSearch */
|
|
private $oDBObjetSearch;
|
|
|
|
/**
|
|
* SQLObjectQueryBuilder constructor.
|
|
*
|
|
* @param \DBObjectSearch $oDBObjetSearch
|
|
*/
|
|
public function __construct($oDBObjetSearch)
|
|
{
|
|
$this->oDBObjetSearch = $oDBObjetSearch;
|
|
}
|
|
|
|
/**
|
|
* @param array $aAttToLoad
|
|
* @param bool $bGetCount
|
|
* @param array $aModifierProperties
|
|
* @param array $aGroupByExpr
|
|
* @param array $aSelectedClasses
|
|
* @param array $aSelectExpr
|
|
*
|
|
* @return null|SQLObjectQuery
|
|
* @throws \CoreException
|
|
*/
|
|
public function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
|
{
|
|
if ($bGetCount || !is_null($aGroupByExpr))
|
|
{
|
|
// Avoid adding all the fields for counts or "group by" requests
|
|
$aAttToLoad = array();
|
|
foreach ($this->oDBObjetSearch->GetSelectedClasses() as $sClassAlias => $sClass)
|
|
{
|
|
$aAttToLoad[$sClassAlias] = array();
|
|
}
|
|
}
|
|
|
|
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $aAttToLoad);
|
|
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, array(), $aGroupByExpr, $aSelectExpr);
|
|
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
/**
|
|
* @return \SQLObjectQuery|null
|
|
* @throws \CoreException
|
|
*/
|
|
public function MakeSQLObjectDeleteQuery()
|
|
{
|
|
$aModifierProperties = MetaModel::MakeModifierProperties($this->oDBObjetSearch);
|
|
$aAttToLoad = array($this->oDBObjetSearch->GetClassAlias() => array());
|
|
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, null, null, null, $aAttToLoad);
|
|
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, array());
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
/**
|
|
* @param array $aValues an array of $sAttCode => $value
|
|
*
|
|
* @return \SQLObjectQuery|null
|
|
* @throws \CoreException
|
|
*/
|
|
public function MakeSQLObjectUpdateQuery($aValues)
|
|
{
|
|
$aModifierProperties = MetaModel::MakeModifierProperties($this->oDBObjetSearch);
|
|
$aRequested = array(); // Requested attributes are the updated attributes
|
|
foreach ($aValues as $sAttCode => $value)
|
|
{
|
|
$aRequested[$sAttCode] = MetaModel::GetAttributeDef($this->oDBObjetSearch->GetClass(), $sAttCode);
|
|
}
|
|
$aAttToLoad = array($this->oDBObjetSearch->GetClassAlias() => $aRequested);
|
|
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, null, null, null, $aAttToLoad);
|
|
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, $aValues);
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
/**
|
|
* @param \QueryBuilderContext $oBuild
|
|
*
|
|
* @return \OQLClassNode
|
|
* @throws \CoreException
|
|
*/
|
|
private function GetOQLClassTree($oBuild)
|
|
{
|
|
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($this->oDBObjetSearch, $oBuild);
|
|
$oOQLClassNode = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
$oOQLClassTreeOptimizer = new OQLClassTreeOptimizer($oOQLClassNode, $oBuild);
|
|
$oOQLClassTreeOptimizer->OptimizeClassTree();
|
|
$oOQLActualClassTreeResolver = new OQLActualClassTreeResolver($oOQLClassNode, $oBuild);
|
|
$oOQLClassNode = $oOQLActualClassTreeResolver->Resolve();
|
|
|
|
return $oOQLClassNode;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \QueryBuilderContext $oBuild
|
|
* @param array $aValues
|
|
* @param array $aGroupByExpr
|
|
* @param array $aSelectExpr
|
|
*
|
|
* @return null|SQLObjectQuery
|
|
* @throws \CoreException
|
|
*/
|
|
private function MakeSQLObjectQueryRoot($oBuild, $aValues = array(), $aGroupByExpr = null, $aSelectExpr = null)
|
|
{
|
|
$oOQLClassNode = $this->GetOQLClassTree($oBuild);
|
|
|
|
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $oOQLClassNode, $aValues);
|
|
|
|
/**
|
|
* Add SQL Level additional information
|
|
*/
|
|
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
|
|
|
if (is_array($aGroupByExpr))
|
|
{
|
|
$aCols = $oBuild->m_oQBExpressions->GetGroupBy();
|
|
$oSQLQuery->SetGroupBy($aCols);
|
|
$oSQLQuery->SetSelect($aCols);
|
|
|
|
if (!empty($aSelectExpr))
|
|
{
|
|
// Get the fields corresponding to the select expressions
|
|
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
|
|
{
|
|
if (key_exists($sAlias, $aSelectExpr))
|
|
{
|
|
$oSQLQuery->AddSelect($sAlias, $oExpr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
|
}
|
|
|
|
// Filter for archive flag
|
|
// Filter tables as late as possible: do not interfere with the optimization process
|
|
$aMandatoryTables = $oBuild->m_oQBExpressions->GetMandatoryTables();
|
|
foreach ($oBuild->GetFilteredTables() as $sTableAlias => $aConditions)
|
|
{
|
|
if ($aMandatoryTables && array_key_exists($sTableAlias, $aMandatoryTables))
|
|
{
|
|
foreach ($aConditions as $oCondition)
|
|
{
|
|
$oSQLQuery->AddCondition($oCondition);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \QueryBuilderContext $oBuild
|
|
* @param \OQLClassNode $oOQLClassNode
|
|
* @param array $aValues
|
|
*
|
|
* @return \SQLObjectQuery
|
|
* @throws \CoreException
|
|
*/
|
|
private function MakeSQLObjectQuery($oBuild, $oOQLClassNode, $aValues)
|
|
{
|
|
$oSQLQuery = $this->MakeSQLObjectQueryNode($oBuild, $oOQLClassNode, $aValues);
|
|
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \QueryBuilderContext $oBuild
|
|
* @param \OQLClassNode $oOQLClassNode
|
|
* @param array $aValues
|
|
*
|
|
* @return \SQLObjectQuery
|
|
* @throws \CoreException
|
|
*/
|
|
private function MakeSQLObjectQueryNode($oBuild, $oOQLClassNode, $aValues)
|
|
{
|
|
$sClass = $oOQLClassNode->GetNodeClass();
|
|
$sTable = MetaModel::DBGetTable($sClass);
|
|
$sClassAlias = $oOQLClassNode->GetNodeClassAlias();
|
|
$sSelectedClassAlias = $oOQLClassNode->GetOQLClassAlias();
|
|
$bIsOnQueriedClass = array_key_exists($sSelectedClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
|
|
$aExpectedAttributes = $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias);
|
|
$oSelectedIdField = null;
|
|
|
|
$aTranslation = array();
|
|
$aUpdateValues = array();
|
|
$oIdField = new FieldExpressionResolved(MetaModel::DBGetKey($sClass), $sClassAlias);
|
|
$aTranslation[$sClassAlias]['id'] = $oIdField;
|
|
if ($bIsOnQueriedClass)
|
|
{
|
|
// Add this field to the list of queried fields (required for the COUNT to work fine)
|
|
$oSelectedIdField = $oIdField;
|
|
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
|
|
{
|
|
if (!array_key_exists($sAttCode, $aValues))
|
|
{
|
|
continue;
|
|
}
|
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
// Skip this attribute if not made of SQL columns
|
|
if (count($oAttDef->GetSQLExpressions()) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
foreach ($oAttDef->GetSQLValues($aValues[$sAttCode]) as $sColumn => $sValue)
|
|
{
|
|
$aUpdateValues[$sColumn] = $sValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
$oBaseSQLQuery = new SQLObjectQuery($sTable, $sClassAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
|
|
|
|
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
|
|
{
|
|
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
|
{
|
|
continue;
|
|
}
|
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
|
{
|
|
if (!empty($sColId))
|
|
{
|
|
// Multi column attributes
|
|
$oBuild->m_oQBExpressions->AddSelect($sSelectedClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
|
|
}
|
|
$oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sClassAlias);
|
|
/**
|
|
* @var string $sPluginClass
|
|
* @var iQueryModifier $oQueryModifier
|
|
*/
|
|
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
|
{
|
|
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, $sColId, $oFieldSQLExp, $oBaseSQLQuery);
|
|
}
|
|
$aTranslation[$sClassAlias][$sAttCode.$sColId] = $oFieldSQLExp;
|
|
}
|
|
}
|
|
|
|
// Translate the selected columns
|
|
//
|
|
$oBuild->m_oQBExpressions->Translate($aTranslation, false);
|
|
|
|
// Filter out archived records
|
|
//
|
|
if (MetaModel::IsArchivable($sClass))
|
|
{
|
|
if (!$oBuild->GetRootFilter()->GetArchiveMode())
|
|
{
|
|
$bIsOnJoinedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetJoinedClasses());
|
|
if ($bIsOnJoinedClass)
|
|
{
|
|
$oNotArchived = new BinaryExpression(new FieldExpressionResolved('archive_flag', $sClassAlias), '=', new ScalarExpression(0));
|
|
$oBuild->AddFilteredTable($sClassAlias, $oNotArchived);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add Joins
|
|
$aJoins = $oOQLClassNode->GetJoins();
|
|
foreach ($aJoins as $aJoin)
|
|
{
|
|
foreach ($aJoin as $oOQLJoin)
|
|
{
|
|
$oJoinedClassNode = $oOQLJoin->GetOOQLClassNode();
|
|
$oJoinedSQLQuery = $this->MakeSQLObjectQueryNode($oBuild, $oJoinedClassNode, $aValues);
|
|
$oOQLJoin->AddToSQLObjectQuery($oBuild, $oBaseSQLQuery, $oJoinedSQLQuery);
|
|
}
|
|
}
|
|
|
|
return $oBaseSQLQuery;
|
|
}
|
|
|
|
} |