mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
742 lines
19 KiB
PHP
742 lines
19 KiB
PHP
<?php
|
|
// Copyright (C) 2015-2017 Combodo SARL
|
|
//
|
|
// This file is part of iTop.
|
|
//
|
|
// iTop is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// iTop is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
|
|
/**
|
|
* A union of DBObjectSearches
|
|
*
|
|
* This search class represent an union over a collection of DBObjectSearch.
|
|
* For clarity purpose, since only the constructor vary between DBObjectSearch and DBUnionSearch, all the API is documented on the common ancestor: DBSearch
|
|
* Please refer to DBSearch's documentation
|
|
*
|
|
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
|
* @license http://opensource.org/licenses/AGPL-3.0
|
|
*
|
|
*
|
|
* @package iTopORM
|
|
* @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only.
|
|
* @api
|
|
* @see DBSearch
|
|
* @see DBObjectSearch
|
|
*/
|
|
|
|
class DBUnionSearch extends DBSearch
|
|
{
|
|
protected $aSearches; // source queries
|
|
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
|
|
|
|
/**
|
|
* DBUnionSearch constructor.
|
|
*
|
|
* @api
|
|
*
|
|
* @param $aSearches
|
|
*
|
|
* @throws CoreException
|
|
*/
|
|
public function __construct($aSearches)
|
|
{
|
|
if (count ($aSearches) == 0)
|
|
{
|
|
throw new CoreException('A DBUnionSearch must be made of at least one search');
|
|
}
|
|
|
|
$this->aSearches = array();
|
|
foreach ($aSearches as $oSearch)
|
|
{
|
|
if ($oSearch instanceof DBUnionSearch)
|
|
{
|
|
foreach ($oSearch->aSearches as $oSubSearch)
|
|
{
|
|
$this->aSearches[] = $oSubSearch->DeepClone();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$this->aSearches[] = $oSearch->DeepClone();
|
|
}
|
|
}
|
|
|
|
$this->ComputeSelectedClasses();
|
|
}
|
|
|
|
public function AllowAllData()
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->AllowAllData();
|
|
}
|
|
}
|
|
public function IsAllDataAllowed()
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
if ($oSearch->IsAllDataAllowed() === false) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function SetArchiveMode($bEnable)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->SetArchiveMode($bEnable);
|
|
}
|
|
parent::SetArchiveMode($bEnable);
|
|
}
|
|
|
|
public function SetShowObsoleteData($bShow)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->SetShowObsoleteData($bShow);
|
|
}
|
|
parent::SetShowObsoleteData($bShow);
|
|
}
|
|
|
|
/**
|
|
* Find the lowest common ancestor for each of the selected class
|
|
*/
|
|
protected function ComputeSelectedClasses()
|
|
{
|
|
// 1 - Collect all the column/classes
|
|
$aColumnToClasses = array();
|
|
foreach ($this->aSearches as $iPos => $oSearch)
|
|
{
|
|
$aSelected = array_values($oSearch->GetSelectedClasses());
|
|
|
|
if ($iPos != 0)
|
|
{
|
|
if (count($aSelected) < count($aColumnToClasses))
|
|
{
|
|
throw new Exception('Too few selected classes in the subquery #'.($iPos+1));
|
|
}
|
|
if (count($aSelected) > count($aColumnToClasses))
|
|
{
|
|
throw new Exception('Too many selected classes in the subquery #'.($iPos+1));
|
|
}
|
|
}
|
|
|
|
foreach ($aSelected as $iColumn => $sClass)
|
|
{
|
|
$aColumnToClasses[$iColumn][] = $sClass;
|
|
}
|
|
}
|
|
|
|
// 2 - Build the index column => alias
|
|
$oFirstSearch = $this->aSearches[0];
|
|
$aColumnToAlias = array_keys($oFirstSearch->GetSelectedClasses());
|
|
|
|
// 3 - Compute alias => lowest common ancestor
|
|
$this->aSelectedClasses = array();
|
|
foreach ($aColumnToClasses as $iColumn => $aClasses)
|
|
{
|
|
$sAlias = $aColumnToAlias[$iColumn];
|
|
$sAncestor = MetaModel::GetLowestCommonAncestor($aClasses);
|
|
if (is_null($sAncestor))
|
|
{
|
|
throw new Exception('Could not find a common ancestor for the column '.($iColumn+1).' (Classes: '.implode(', ', $aClasses).')');
|
|
}
|
|
$this->aSelectedClasses[$sAlias] = $sAncestor;
|
|
}
|
|
}
|
|
|
|
public function GetSearches()
|
|
{
|
|
return $this->aSearches;
|
|
}
|
|
|
|
public function GetFirstJoinedClass()
|
|
{
|
|
return $this->GetClass();
|
|
}
|
|
|
|
/**
|
|
* Limited to the selected classes
|
|
*/
|
|
public function GetClassName($sAlias)
|
|
{
|
|
if (array_key_exists($sAlias, $this->aSelectedClasses))
|
|
{
|
|
return $this->aSelectedClasses[$sAlias];
|
|
}
|
|
else
|
|
{
|
|
throw new CoreException("Invalid class alias '$sAlias'");
|
|
}
|
|
}
|
|
|
|
public function GetClass()
|
|
{
|
|
return reset($this->aSelectedClasses);
|
|
}
|
|
|
|
public function GetClassAlias()
|
|
{
|
|
reset($this->aSelectedClasses);
|
|
return key($this->aSelectedClasses);
|
|
}
|
|
|
|
|
|
/**
|
|
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
|
* Defaults to the first selected class
|
|
* Only the selected classes can be changed
|
|
*/
|
|
public function ChangeClass($sNewClass, $sAlias = null)
|
|
{
|
|
if (is_null($sAlias))
|
|
{
|
|
$sAlias = $this->GetClassAlias();
|
|
}
|
|
elseif (!array_key_exists($sAlias, $this->aSelectedClasses))
|
|
{
|
|
// discard silently - necessary when recursing (??? copied from DBObjectSearch)
|
|
return;
|
|
}
|
|
|
|
// 1 - identify the impacted column
|
|
$iColumn = array_search($sAlias, array_keys($this->aSelectedClasses));
|
|
|
|
// 2 - change for each search
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
|
$sSearchAlias = $aSearchAliases[$iColumn];
|
|
$oSearch->ChangeClass($sNewClass, $sSearchAlias);
|
|
}
|
|
|
|
// 3 - record the change
|
|
$this->aSelectedClasses[$sAlias] = $sNewClass;
|
|
}
|
|
|
|
public function GetSelectedClasses()
|
|
{
|
|
return $this->aSelectedClasses;
|
|
}
|
|
|
|
/**
|
|
* @param array $aSelectedClasses array of aliases
|
|
* @throws CoreException
|
|
*/
|
|
public function SetSelectedClasses($aSelectedClasses)
|
|
{
|
|
// 1 - change for each search
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
// Throws an exception if not valid
|
|
$oSearch->SetSelectedClasses($aSelectedClasses);
|
|
}
|
|
// 2 - update the lowest common ancestors
|
|
$this->ComputeSelectedClasses();
|
|
}
|
|
|
|
/**
|
|
* Change any alias of the query tree
|
|
*
|
|
* @param $sOldName
|
|
* @param $sNewName
|
|
* @return bool True if the alias has been found and changed
|
|
*/
|
|
public function RenameAlias($sOldName, $sNewName)
|
|
{
|
|
$bRet = false;
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$bRet = $oSearch->RenameAlias($sOldName, $sNewName) || $bRet;
|
|
}
|
|
return $bRet;
|
|
}
|
|
|
|
public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array())
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation);
|
|
}
|
|
}
|
|
|
|
public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->TranslateConditions($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function IsAny()
|
|
{
|
|
$bIsAny = true;
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
if (!$oSearch->IsAny())
|
|
{
|
|
$bIsAny = false;
|
|
break;
|
|
}
|
|
}
|
|
return $bIsAny;
|
|
}
|
|
|
|
public function ResetCondition()
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->ResetCondition();
|
|
}
|
|
}
|
|
|
|
public function MergeConditionExpression($oExpression)
|
|
{
|
|
$aAliases = array_keys($this->aSelectedClasses);
|
|
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
|
{
|
|
$oClonedExpression = $oExpression->DeepClone();
|
|
if ($iSearchIndex != 0)
|
|
{
|
|
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
|
{
|
|
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
|
}
|
|
}
|
|
$oSearch->MergeConditionExpression($oClonedExpression);
|
|
}
|
|
}
|
|
|
|
public function AddConditionExpression($oExpression)
|
|
{
|
|
$aAliases = array_keys($this->aSelectedClasses);
|
|
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
|
{
|
|
$oClonedExpression = $oExpression->DeepClone();
|
|
if ($iSearchIndex != 0)
|
|
{
|
|
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
|
|
{
|
|
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
|
|
}
|
|
}
|
|
$oSearch->AddConditionExpression($oClonedExpression);
|
|
}
|
|
}
|
|
|
|
public function AddNameCondition($sName)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->AddNameCondition($sName);
|
|
}
|
|
}
|
|
|
|
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->AddCondition($sFilterCode, $value, $sOpCode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Specify a condition on external keys or link sets
|
|
* @param String sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
|
* Example: infra_list->ci_id->location_id->country
|
|
* @param Object value The value to match (can be an array => IN(val1, val2...)
|
|
* @return void
|
|
*/
|
|
public function AddConditionAdvanced($sAttSpec, $value)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->AddConditionAdvanced($sAttSpec, $value);
|
|
}
|
|
}
|
|
|
|
public function AddCondition_FullText($sFullText)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->AddCondition_FullText($sFullText);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param DBObjectSearch $oFilter
|
|
* @param $sExtKeyAttCode
|
|
* @param int $iOperatorCode
|
|
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
|
*/
|
|
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oConditionFilter = $oFilter->DeepClone();
|
|
$oSearch->AddCondition_PointingTo($oConditionFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param DBObjectSearch $oFilter
|
|
* @param $sForeignExtKeyAttCode
|
|
* @param int $iOperatorCode
|
|
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
|
*/
|
|
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oConditionFilter = $oFilter->DeepClone();
|
|
$oSearch->AddCondition_ReferencedBy($oConditionFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
|
}
|
|
}
|
|
|
|
public function Filter($sClassAlias, DBSearch $oFilter)
|
|
{
|
|
$aSearches = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
|
|
}
|
|
return new DBUnionSearch($aSearches);
|
|
}
|
|
|
|
public function Intersect(DBSearch $oFilter)
|
|
{
|
|
$aSearches = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aSearches[] = $oSearch->Intersect($oFilter);
|
|
}
|
|
return new DBUnionSearch($aSearches);
|
|
}
|
|
|
|
public function SetInternalParams($aParams)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->SetInternalParams($aParams);
|
|
}
|
|
}
|
|
|
|
public function GetInternalParams()
|
|
{
|
|
$aParams = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aParams = array_merge($oSearch->GetInternalParams(), $aParams);
|
|
}
|
|
return $aParams;
|
|
}
|
|
|
|
public function GetQueryParams($bExcludeMagicParams = true)
|
|
{
|
|
$aParams = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aParams = array_merge($oSearch->GetQueryParams($bExcludeMagicParams), $aParams);
|
|
}
|
|
return $aParams;
|
|
}
|
|
|
|
public function ListConstantFields()
|
|
{
|
|
// Somewhat complex to implement for unions, for a poor benefit
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Turn the parameters (:xxx) into scalar values in order to easily
|
|
* serialize a search
|
|
*/
|
|
public function ApplyParameters($aArgs)
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->ApplyParameters($aArgs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overloads for query building
|
|
*/
|
|
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
|
|
{
|
|
$aSubQueries = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams, $bWithAllowAllFlag);
|
|
}
|
|
$sRet = implode(' UNION ', $aSubQueries);
|
|
return $sRet;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
* @see DBSearch::ToJSON()
|
|
*/
|
|
public function ToJSON()
|
|
{
|
|
$sRet = array('unions' => array());
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$sRet['unions'][] = $oSearch->ToJSON();
|
|
}
|
|
return $sRet;
|
|
}
|
|
|
|
/**
|
|
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
|
|
*
|
|
* @return \DBUnionSearch
|
|
* @throws \CoreException
|
|
*/
|
|
public function RemoveDuplicateQueries()
|
|
{
|
|
$aQueries = array();
|
|
$aSearches = array();
|
|
|
|
foreach ($this->GetSearches() as $oTmpSearch)
|
|
{
|
|
$sQuery = $oTmpSearch->ToOQL(true);
|
|
if (!in_array($sQuery, $aQueries))
|
|
{
|
|
$aQueries[] = $sQuery;
|
|
$aSearches[] = $oTmpSearch;
|
|
}
|
|
}
|
|
|
|
$oNewSearch = new DBUnionSearch($aSearches);
|
|
|
|
return $oNewSearch;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Construction of the SQL queries
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function MakeDeleteQuery($aArgs = array())
|
|
{
|
|
throw new Exception('MakeDeleteQuery is not implemented for the unions!');
|
|
}
|
|
|
|
public function MakeUpdateQuery($aValues, $aArgs = array())
|
|
{
|
|
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
|
}
|
|
|
|
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
|
{
|
|
if (count($this->aSearches) == 1)
|
|
{
|
|
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
|
}
|
|
|
|
$aSQLQueries = array();
|
|
$aAliases = array_keys($this->aSelectedClasses);
|
|
$aQueryAttToLoad = null;
|
|
$aUnionQuerySelectExpr = array();
|
|
foreach ($this->aSearches as $iSearch => $oSearch)
|
|
{
|
|
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
|
|
|
// The selected classes from the query build perspective are the lowest common ancestors amongst the various queries
|
|
// (used when it comes to determine which attributes must be selected)
|
|
$aSearchSelectedClasses = array();
|
|
foreach ($aSearchAliases as $iColumn => $sSearchAlias)
|
|
{
|
|
$sAlias = $aAliases[$iColumn];
|
|
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
|
|
}
|
|
|
|
if ($bGetCount)
|
|
{
|
|
// Select only ids for the count to allow optimization of joins
|
|
foreach($aSearchAliases as $sSearchAlias)
|
|
{
|
|
$aQueryAttToLoad[$sSearchAlias] = array();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is_null($aAttToLoad))
|
|
{
|
|
$aQueryAttToLoad = null;
|
|
}
|
|
else
|
|
{
|
|
// (Eventually) Transform the aliases
|
|
$aQueryAttToLoad = array();
|
|
foreach($aAttToLoad as $sAlias => $aAttributes)
|
|
{
|
|
$iColumn = array_search($sAlias, $aAliases);
|
|
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
|
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_null($aGroupByExpr))
|
|
{
|
|
$aQueryGroupByExpr = null;
|
|
}
|
|
else
|
|
{
|
|
// Clone (and eventually transform) the group by expressions
|
|
$aQueryGroupByExpr = array();
|
|
$aTranslationData = array();
|
|
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
|
foreach ($aAliases as $iColumn => $sAlias)
|
|
{
|
|
$sQueryAlias = $aQueryColumns[$iColumn];
|
|
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
|
$aQueryGroupByExpr[$sAlias.'id'] = new FieldExpression('id', $sQueryAlias);
|
|
}
|
|
foreach ($aGroupByExpr as $sExpressionAlias => $oExpression)
|
|
{
|
|
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
|
}
|
|
}
|
|
|
|
if (is_null($aSelectExpr))
|
|
{
|
|
$aQuerySelectExpr = null;
|
|
}
|
|
else
|
|
{
|
|
$aQuerySelectExpr = array();
|
|
$aTranslationData = array();
|
|
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
|
foreach($aAliases as $iColumn => $sAlias)
|
|
{
|
|
$sQueryAlias = $aQueryColumns[$iColumn];
|
|
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
|
}
|
|
foreach($aSelectExpr as $sExpressionAlias => $oExpression)
|
|
{
|
|
$oExpression->Browse(function ($oNode) use (&$aQuerySelectExpr, &$aTranslationData)
|
|
{
|
|
if ($oNode instanceof FieldExpression)
|
|
{
|
|
$sAlias = $oNode->GetParent()."__".$oNode->GetName();
|
|
if (!key_exists($sAlias, $aQuerySelectExpr))
|
|
{
|
|
$aQuerySelectExpr[$sAlias] = $oNode->Translate($aTranslationData, false, false);
|
|
}
|
|
$aTranslationData[$oNode->GetParent()][$oNode->GetName()] = new FieldExpression($sAlias);
|
|
}
|
|
});
|
|
// Only done for the first select as aliases are named after the first query
|
|
if (!array_key_exists($sExpressionAlias, $aUnionQuerySelectExpr))
|
|
{
|
|
$aUnionQuerySelectExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
|
}
|
|
}
|
|
}
|
|
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
|
|
if (count($aSearchAliases) > 1)
|
|
{
|
|
// Necessary to make sure that selected columns will match throughout all the queries
|
|
// (default order of selected fields depending on the order of JOINS)
|
|
$oSubQuery->SortSelectedFields();
|
|
}
|
|
$aSQLQueries[] = $oSubQuery;
|
|
}
|
|
|
|
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr, $aUnionQuerySelectExpr);
|
|
//MyHelpers::var_dump_html($oSQLQuery, true);
|
|
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
|
|
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
|
|
return $oSQLQuery;
|
|
}
|
|
|
|
function GetExpectedArguments()
|
|
{
|
|
$aVariableCriteria = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aVariableCriteria = array_merge($aVariableCriteria, $oSearch->GetExpectedArguments());
|
|
}
|
|
|
|
return $aVariableCriteria;
|
|
}
|
|
/**
|
|
* @return \Expression
|
|
*/
|
|
public function GetCriteria()
|
|
{
|
|
// We're at the limit here
|
|
$oSearch = reset($this->aSearches);
|
|
return $oSearch->GetCriteria();
|
|
}
|
|
|
|
protected function IsDataFiltered()
|
|
{
|
|
$bIsAllDataFiltered = true;
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
if (!$oSearch->IsDataFiltered())
|
|
{
|
|
$bIsAllDataFiltered = false;
|
|
break;
|
|
}
|
|
}
|
|
return $bIsAllDataFiltered;
|
|
}
|
|
|
|
protected function SetDataFiltered()
|
|
{
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$oSearch->SetDataFiltered();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
|
|
{
|
|
$sInParamName = $this->GenerateUniqueParamName();
|
|
foreach ($this->aSearches as $iSearchIndex => $oSearch)
|
|
{
|
|
$oFieldExpression = new FieldExpression($sFilterCode, $oSearch->GetClassAlias());
|
|
|
|
$sOperator = $bPositiveMatch ? 'IN' : 'NOT IN';
|
|
|
|
$oParamExpression = new VariableExpression($sInParamName);
|
|
$oSearch->GetInternalParamsByRef()[$sInParamName] = $aValues;
|
|
|
|
$oListExpression = new ListExpression(array($oParamExpression));
|
|
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oListExpression);
|
|
$oSearch->AddConditionExpression($oInCondition);
|
|
}
|
|
}
|
|
|
|
public function ListParameters()
|
|
{
|
|
$aParameters = array();
|
|
foreach ($this->aSearches as $oSearch)
|
|
{
|
|
$aParameters = array_merge($aParameters, $oSearch->ListParameters());
|
|
}
|
|
return $aParameters;
|
|
}
|
|
}
|