mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-20 00:58:48 +02:00
N°1485 - Search: "Undefined" on hierarchical key not working
SVN:trunk[5873]
This commit is contained in:
@@ -550,27 +550,27 @@ class BinaryExpression extends Expression
|
||||
$oAttDef = $oRightExpr->GetAttDef($oSearch->GetJoinedClasses());
|
||||
$aCriteriaLeft = $oLeftExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
|
||||
|
||||
$aCriteria = array_merge($aCriteriaRight, $aCriteriaLeft);
|
||||
// switch left and right expressions so reverse the operator
|
||||
// Note that the operation is the same so < becomes > and not >=
|
||||
switch ($this->GetOperator())
|
||||
{
|
||||
case '>':
|
||||
$aCriteria['operator'] = '<';
|
||||
$sOperator = '<';
|
||||
break;
|
||||
case '<':
|
||||
$aCriteria['operator'] = '>';
|
||||
$sOperator = '>';
|
||||
break;
|
||||
case '>=':
|
||||
$aCriteria['operator'] = '<=';
|
||||
$sOperator = '<=';
|
||||
break;
|
||||
case '<=':
|
||||
$aCriteria['operator'] = '>=';
|
||||
$sOperator = '>=';
|
||||
break;
|
||||
default:
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
$sOperator = $this->GetOperator();
|
||||
break;
|
||||
}
|
||||
$aCriteria = self::MergeCriteria($aCriteriaRight, $aCriteriaLeft, $sOperator);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -579,8 +579,7 @@ class BinaryExpression extends Expression
|
||||
$oAttDef = $oLeftExpr->GetAttDef($oSearch->GetJoinedClasses());
|
||||
$aCriteriaRight = $oRightExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
|
||||
|
||||
$aCriteria = array_merge($aCriteriaLeft, $aCriteriaRight);
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
$aCriteria = self::MergeCriteria($aCriteriaLeft, $aCriteriaRight, $this->GetOperator());
|
||||
}
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
$aCriteria['label'] = $this->Display($oSearch, $aArgs, $oAttDef);
|
||||
@@ -593,6 +592,30 @@ class BinaryExpression extends Expression
|
||||
|
||||
return $aCriteria;
|
||||
}
|
||||
|
||||
protected static function MergeCriteria($aCriteriaLeft, $aCriteriaRight, $sOperator)
|
||||
{
|
||||
$aCriteriaOverride = array();
|
||||
$aCriteriaOverride['operator'] = $sOperator;
|
||||
if ($sOperator == 'OR')
|
||||
{
|
||||
if (isset($aCriteriaLeft['ref']) && isset($aCriteriaRight['ref']) && ($aCriteriaLeft['ref'] == $aCriteriaRight['ref']))
|
||||
{
|
||||
if (isset($aCriteriaLeft['widget']) && isset($aCriteriaRight['widget']) && ($aCriteriaLeft['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY) && ($aCriteriaRight['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY))
|
||||
{
|
||||
$aCriteriaOverride['operator'] = 'IN';
|
||||
$aCriteriaOverride['is_hierarchical'] = true;
|
||||
|
||||
if (isset($aCriteriaLeft['values']) && isset($aCriteriaRight['values']))
|
||||
{
|
||||
$aCriteriaOverride['values'] = array_merge($aCriteriaLeft['values'], $aCriteriaRight['values']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($aCriteriaLeft, $aCriteriaRight, $aCriteriaOverride);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -697,7 +720,11 @@ class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTarget = $oAttDef->GetTargetClass();
|
||||
$oObj = MetaModel::GetObject($sTarget, $this->m_value);
|
||||
$oObj = MetaModel::GetObject($sTarget, $this->m_value, false);
|
||||
if (empty($oObj))
|
||||
{
|
||||
return Dict::S('Enum:Undefined');
|
||||
}
|
||||
|
||||
return $oObj->Get("friendlyname");
|
||||
} catch (CoreException $e)
|
||||
|
||||
@@ -216,95 +216,23 @@ class CriterionToOQL extends CriterionConversionAbstract
|
||||
return "1";
|
||||
}
|
||||
|
||||
$bFilterOnUndefined = false;
|
||||
$oAttDef = null;
|
||||
try
|
||||
{
|
||||
$aAttributeDefs = MetaModel::ListAttributeDefs($sClass);
|
||||
if (array_key_exists($sAttCode, $aAttributeDefs))
|
||||
{
|
||||
$oAttDef = $aAttributeDefs[$sAttCode];
|
||||
if ($oAttDef instanceof AttributeEnum)
|
||||
{
|
||||
$aAllowedValues = SearchForm::GetFieldAllowedValues($oAttDef);
|
||||
if (array_key_exists('values', $aAllowedValues))
|
||||
{
|
||||
// Can't invert the test if NULL is allowed
|
||||
if (!$oAttDef->IsNullAllowed())
|
||||
{
|
||||
$aAllowedValues = $aAllowedValues['values'];
|
||||
if (count($aValues) == count($aAllowedValues))
|
||||
{
|
||||
// All entries are selected
|
||||
return "1";
|
||||
}
|
||||
// more selected values than remaining so use NOT IN
|
||||
else
|
||||
{
|
||||
if (count($aValues) > (count($aAllowedValues) / 2))
|
||||
{
|
||||
foreach($aValues as $aValue)
|
||||
{
|
||||
unset($aAllowedValues[$aValue['value']]);
|
||||
}
|
||||
$sInList = implode("','", array_keys($aAllowedValues));
|
||||
|
||||
return "({$sRef} NOT IN ('$sInList'))";
|
||||
}
|
||||
}
|
||||
}
|
||||
// search for "undefined"
|
||||
for($i = 0; $i < count($aValues); $i++)
|
||||
{
|
||||
$aValue = $aValues[$i];
|
||||
if (isset($aValue['value']) && ($aValue['value'] === 'null'))
|
||||
{
|
||||
$bFilterOnUndefined = true;
|
||||
unset($aValues[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
}
|
||||
|
||||
$aInValues = array();
|
||||
foreach($aValues as $aValue)
|
||||
catch (\CoreException $e)
|
||||
{
|
||||
$aInValues[] = $aValue['value'];
|
||||
return "1";
|
||||
}
|
||||
$sInList = implode("','", $aInValues);
|
||||
|
||||
if ($bFilterOnUndefined)
|
||||
if (array_key_exists($sAttCode, $aAttributeDefs))
|
||||
{
|
||||
$sFilterOnUndefined = "ISNULL({$sRef})";
|
||||
if (count($aValues) === 0)
|
||||
{
|
||||
$sCondition = $sFilterOnUndefined;
|
||||
}
|
||||
elseif (count($aInValues) == 1)
|
||||
{
|
||||
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
|
||||
$sCondition = "((({$sRef} = '$sInList') OR {$sFilterOnUndefined}) AND 1)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
|
||||
$sCondition = "(({$sRef} IN ('$sInList') OR {$sFilterOnUndefined}) AND 1)";
|
||||
}
|
||||
}
|
||||
elseif (count($aInValues) == 1)
|
||||
{
|
||||
$sCondition = "({$sRef} = '$sInList')";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCondition = "({$sRef} IN ('$sInList'))";
|
||||
$oAttDef = $aAttributeDefs[$sAttCode];
|
||||
}
|
||||
|
||||
// Hierarchical keys
|
||||
$sHierarchicalKeyCode = false;
|
||||
$sTargetClass = '';
|
||||
if (isset($oAttDef) && $oAttDef->IsExternalKey() && (!isset($aCriteria['is_hierarchical']) || ($aCriteria['is_hierarchical'] !== false)))
|
||||
{
|
||||
if ($oAttDef instanceof AttributeExternalKey)
|
||||
@@ -321,26 +249,126 @@ class CriterionToOQL extends CriterionConversionAbstract
|
||||
try
|
||||
{
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$sFilterAlias = $oFilter->GetClassAlias();
|
||||
$sCondition = str_replace("$sRef", $sFilterAlias.'.id', $sCondition);
|
||||
$oCondition = Expression::FromOQL($sCondition);
|
||||
$oFilter->AddConditionExpression($oCondition);
|
||||
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
// Use the 'below' operator by default
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
$sCondition = '1';
|
||||
}
|
||||
} catch (Exception $e)
|
||||
}
|
||||
catch (\CoreException $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
$sFilterOnUndefined = '';
|
||||
if ($oAttDef instanceof AttributeEnum)
|
||||
{
|
||||
$aAllowedValues = SearchForm::GetFieldAllowedValues($oAttDef);
|
||||
if (array_key_exists('values', $aAllowedValues))
|
||||
{
|
||||
// Can't invert the test if NULL is allowed
|
||||
if (!$oAttDef->IsNullAllowed())
|
||||
{
|
||||
$aAllowedValues = $aAllowedValues['values'];
|
||||
if (count($aValues) == count($aAllowedValues))
|
||||
{
|
||||
// All entries are selected
|
||||
return "1";
|
||||
}
|
||||
// more selected values than remaining so use NOT IN
|
||||
else
|
||||
{
|
||||
if (count($aValues) > (count($aAllowedValues) / 2))
|
||||
{
|
||||
foreach($aValues as $aValue)
|
||||
{
|
||||
unset($aAllowedValues[$aValue['value']]);
|
||||
}
|
||||
$sInList = implode("','", array_keys($aAllowedValues));
|
||||
|
||||
return "({$sRef} NOT IN ('$sInList'))";
|
||||
}
|
||||
}
|
||||
}
|
||||
// search for "undefined"
|
||||
for($i = 0; $i < count($aValues); $i++)
|
||||
{
|
||||
$aValue = $aValues[$i];
|
||||
if (isset($aValue['value']) && ($aValue['value'] === 'null'))
|
||||
{
|
||||
$sFilterOnUndefined = "ISNULL({$sRef})";
|
||||
unset($aValues[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
// search for "undefined"
|
||||
for($i = 0; $i < count($aValues); $i++)
|
||||
{
|
||||
$aValue = $aValues[$i];
|
||||
if (isset($aValue['value']) && ($aValue['value'] === '0'))
|
||||
{
|
||||
$sFilterOnUndefined = "({$sRef} = '0')";
|
||||
unset($aValues[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aInValues = array();
|
||||
foreach($aValues as $aValue)
|
||||
{
|
||||
$aInValues[] = $aValue['value'];
|
||||
}
|
||||
$sInList = implode("','", $aInValues);
|
||||
|
||||
$sCondition = '1';
|
||||
if (count($aInValues) == 1)
|
||||
{
|
||||
$sCondition = "({$sRef} = '$sInList')";
|
||||
}
|
||||
elseif (count($aInValues) > 1)
|
||||
{
|
||||
$sCondition = "({$sRef} IN ('$sInList'))";
|
||||
}
|
||||
|
||||
// Hierarchical keys
|
||||
try
|
||||
{
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
// Add all the joins for hierarchical key
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$sFilterAlias = $oFilter->GetClassAlias();
|
||||
// Filter on hierarchy
|
||||
$sCondition = str_replace("$sRef", $sFilterAlias.'.id', $sCondition);
|
||||
$oCondition = Expression::FromOQL($sCondition);
|
||||
$oFilter->AddConditionExpression($oCondition);
|
||||
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
// Use the 'below' operator by default
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
$oCriteria = $oSearch->GetCriteria();
|
||||
$aArgs = MetaModel::PrepareQueryArguments(array(), $oSearch->GetInternalParams());
|
||||
$oSearch->ResetCondition();
|
||||
$sCondition = $oCriteria->Render($aArgs);
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
}
|
||||
|
||||
if (!empty($sFilterOnUndefined))
|
||||
{
|
||||
if (count($aValues) === 0)
|
||||
{
|
||||
$sCondition = $sFilterOnUndefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
|
||||
$sCondition = "(({$sCondition} OR {$sFilterOnUndefined}) AND 1)";
|
||||
}
|
||||
}
|
||||
return $sCondition;
|
||||
}
|
||||
|
||||
|
||||
@@ -677,7 +677,7 @@ class CriterionToSearchForm extends CriterionConversionAbstract
|
||||
unset($aCriteria['oql']);
|
||||
break;
|
||||
case 'IN':
|
||||
// Nothing special to do
|
||||
unset($aCriteria['oql']);
|
||||
break;
|
||||
default:
|
||||
// Unknown operator
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Combodo\iTop\Test\UnitTest;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
define('DEBUG_UNIT_TEST', false);
|
||||
define('DEBUG_UNIT_TEST', true);
|
||||
|
||||
class ItopTestCase extends TestCase
|
||||
{
|
||||
|
||||
@@ -432,11 +432,16 @@ class CriterionConversionTest extends ItopDataTestCase
|
||||
'ExpectedOQL' => "SELECT `Server` FROM Server AS `Server` WHERE ISNULL(`Server`.`nb_u`)",
|
||||
'ExpectedCriterion' => array(array('widget' => 'numeric', 'operator' => 'empty')),
|
||||
),
|
||||
'Hierarchical below' => array(
|
||||
'Hierarchical below 1' => array(
|
||||
'OQL' => "SELECT Person AS P JOIN Organization AS Node ON P.org_id = Node.id JOIN Organization AS Root ON Node.parent_id BELOW Root.id WHERE Root.id=1",
|
||||
'ExpectedOQL' => "SELECT `P` FROM Person AS `P` JOIN Organization AS `Node` ON `P`.org_id = `Node`.id JOIN Organization AS `Root` ON `Node`.parent_id BELOW `Root`.id WHERE (`Root`.`id` = '1')",
|
||||
'ExpectedCriterion' => array(array('widget' => 'hierarchical_key')),
|
||||
),
|
||||
'Hierarchical below 2' => array(
|
||||
'OQL' => "SELECT `Organization` FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id = `Organization1`.id JOIN Organization AS `Organization11` ON `Organization1`.parent_id BELOW `Organization11`.id WHERE (((`Organization11`.`id` IN ('2', '4')) OR (`Organization`.`parent_id` = '0')) AND 1)",
|
||||
'ExpectedOQL' => "SELECT `Organization` FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id = `Organization1`.id JOIN Organization AS `Organization11` ON `Organization1`.parent_id BELOW `Organization11`.id WHERE (((`Organization11`.`id` IN ('2', '4')) OR (`Organization`.`parent_id` = '0')) AND 1)",
|
||||
'ExpectedCriterion' => array(array('widget' => 'hierarchical_key')),
|
||||
),
|
||||
'IP range' => array(
|
||||
'OQL' => "SELECT DatacenterDevice AS dev WHERE INET_ATON(dev.managementip) > INET_ATON('10.22.32.224') AND INET_ATON(dev.managementip) < INET_ATON('10.22.32.255')",
|
||||
'ExpectedOQL' => "SELECT `dev` FROM DatacenterDevice AS `dev` WHERE ((INET_ATON(`dev`.`managementip`) < INET_ATON('10.22.32.255')) AND (INET_ATON(`dev`.`managementip`) > INET_ATON('10.22.32.224')))",
|
||||
|
||||
Reference in New Issue
Block a user