mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 10:38:45 +02:00
advanced search: ShorthandExpansion on conversion To SQL
- plus tests - plus implementation of ExternalFieldExpression->Render() SVN:b1312[5760]
This commit is contained in:
@@ -1133,7 +1133,91 @@ class DBObjectSearch extends DBSearch
|
||||
{
|
||||
return $this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
|
||||
}
|
||||
|
||||
|
||||
public function ShorthandExpansion($bClone = false)
|
||||
{
|
||||
if ($bClone)
|
||||
{
|
||||
$oDbObject = $this->DeepClone();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDbObject = $this;
|
||||
}
|
||||
|
||||
|
||||
$callback = function ($oExpression) use ($oDbObject)
|
||||
{
|
||||
if (!$oExpression instanceof ExternalFieldExpression) {
|
||||
return;
|
||||
}
|
||||
/** @var FieldExpression[] $aFieldExpressionsPointingTo */
|
||||
$aFields = $oExpression->GetFields();
|
||||
|
||||
$aExpressionNewConditions = array();
|
||||
$aRealiasingMap = array();
|
||||
foreach ($aFields as $aFieldExpressionPointingTo)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($aFieldExpressionPointingTo['sClass']);
|
||||
$aExpressionNewConditions[] = array(
|
||||
'oFilter' => $oFilter,
|
||||
'sExtKeyAttCode' => $aFieldExpressionPointingTo['sAttCode'],
|
||||
);
|
||||
|
||||
$aRealiasingMap[$aFieldExpressionPointingTo['sClass']] = $aFieldExpressionPointingTo['sAlias'];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* the iteration beleow is weird because wee need to
|
||||
* - iterate in the reverse order
|
||||
* - the iteration access the "index+1" so wee start at "length-1" (which is "count()-2")
|
||||
* - whe stop at the index 1, because the index 0 is merged into the $oDbObject
|
||||
*/
|
||||
for ($i = count($aExpressionNewConditions) - 2; $i > 0; $i--)
|
||||
{
|
||||
$aExpressionNewConditions[$i]['oFilter']->AddCondition_PointingTo(
|
||||
$aExpressionNewConditions[$i+1]['oFilter'],
|
||||
$aExpressionNewConditions[$i]['sExtKeyAttCode'],
|
||||
TREE_OPERATOR_EQUALS,
|
||||
$aRealiasingMap
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$oDbObject->AddCondition_PointingTo(
|
||||
$aExpressionNewConditions[1]['oFilter'],
|
||||
$aExpressionNewConditions[0]['sExtKeyAttCode'],
|
||||
TREE_OPERATOR_EQUALS,
|
||||
$aRealiasingMap
|
||||
);
|
||||
|
||||
foreach ($aRealiasingMap as $sOldAlias => $sNewAlias)
|
||||
{
|
||||
if ($sOldAlias == $sNewAlias)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sOldAlias != $aFields['sAlias'])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$aFields['sAlias'] = $sNewAlias;
|
||||
}
|
||||
|
||||
$oExpression->SetFields($aFields);
|
||||
}; //end of the callback definition
|
||||
|
||||
|
||||
$this->m_oSearchCondition->Browse($callback);
|
||||
|
||||
$this->m_oSearchCondition->Translate(array());
|
||||
|
||||
return $oDbObject;
|
||||
}
|
||||
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
|
||||
{
|
||||
// Currently unused, but could be useful later
|
||||
@@ -1264,7 +1348,20 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
elseif ($oExpression instanceof ExternalFieldOqlExpression)
|
||||
{
|
||||
return new ExternalFieldExpression($oExpression->GetName(), $oExpression->GetExpressions());
|
||||
//TODO : convert FieldOqlExpression[] to FieldExpression[]
|
||||
$aOqlFieldExpression = $oExpression->GetExpressions();
|
||||
$aFields = array();
|
||||
|
||||
foreach ($aOqlFieldExpression as $oOqlFieldExpression)
|
||||
{
|
||||
$aFields[] = array(
|
||||
'sClass' => $oOqlFieldExpression->GetParent(),
|
||||
'sAlias' => $oOqlFieldExpression->GetParent(),
|
||||
'sAttCode' => $oOqlFieldExpression->GetName(),
|
||||
);
|
||||
}
|
||||
|
||||
return new ExternalFieldExpression($oExpression->GetName(), $aFields);
|
||||
}
|
||||
elseif ($oExpression instanceof FieldOqlExpression)
|
||||
{
|
||||
@@ -1534,7 +1631,7 @@ class DBObjectSearch extends DBSearch
|
||||
$aContextData['sRequestUri'] = '';
|
||||
}
|
||||
|
||||
// Need to identify the query
|
||||
// Need to identify the querySELECT `Contact` FROM Contact AS `Contact` JOIN Organization AS `Organization` ON `Contact`.org_id = `Organization`.id JOIN DeliveryModel AS `DeliveryModel` ON `Organization`.deliverymodel_id = `DeliveryModel`.id WHERE ((((Contact.org_id->deliverymodel_id->name = 'Standard support') AND 1) AND 1) AND 1)
|
||||
$sOqlQuery = $oSearch->ToOql(false, null, true);
|
||||
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
|
||||
{
|
||||
@@ -1640,7 +1737,8 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
if (!isset($oSQLQuery))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oSearch = $oSearch->ShorthandExpansion(true);//TODO : check if the clone is really needed (1st param of ShorthandExpansion)
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
||||
|
||||
|
||||
@@ -843,13 +843,56 @@ class FalseExpression extends ScalarExpression
|
||||
|
||||
class ExternalFieldExpression extends UnaryExpression
|
||||
{
|
||||
protected $m_aFieldExpressions = array();
|
||||
/**
|
||||
* @var array[] array containing the shorthand chained fields & their classes
|
||||
* ['sClass'] string the Class
|
||||
* ['sAlias'] string the Class alias
|
||||
* ['sAttCode'] string the attribute code
|
||||
*/
|
||||
protected $m_aFields = array();
|
||||
protected $m_sName;
|
||||
|
||||
public function __construct($sName, $aExpressions)
|
||||
public function __construct($sName, $aFields)
|
||||
{
|
||||
parent::__construct($sName);
|
||||
|
||||
$this->SetFields($aFields);
|
||||
}
|
||||
|
||||
public function SetFields($aFields)
|
||||
{
|
||||
$this->m_aFields = $aFields;
|
||||
}
|
||||
|
||||
public function GetFields()
|
||||
{
|
||||
return $this->m_aFields;
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||
{
|
||||
$aFields = $this->GetFields();
|
||||
$aLastField = end($aFields);
|
||||
|
||||
|
||||
$oRet = new FieldExpression($aLastField['sAttCode'], $aLastField['sAlias']);
|
||||
// $oRet->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aAttCode = array();
|
||||
|
||||
$aFields = $this->GetFields();
|
||||
foreach ($aFields as $field) {
|
||||
$aAttCode[] = $field['sAttCode'];
|
||||
}
|
||||
|
||||
return $aFields[0]['sAlias'] . '.' . implode('->', $aAttCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FieldExpression extends UnaryExpression
|
||||
|
||||
@@ -253,7 +253,7 @@ class ExternalFieldOqlExpression extends ExternalFieldExpression implements Chec
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oFieldOqlExpression->GetName() != 'id')
|
||||
if ($oFieldOqlExpression->GetName() != 'id') //on lastIteration, the id field can be used, but since it is not supporter by IsValidAttCode we must avoid it
|
||||
{
|
||||
if (!$oModelReflection->IsValidAttCode($sClass, $oFieldOqlExpression->GetName()))
|
||||
{
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
namespace Combodo\iTop\Test\UnitTest\Core\Oql;
|
||||
|
||||
use OqlInterpreter;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class OqlInterpreterTest extends ItopTestCase
|
||||
class OqlInterpreterTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
@@ -21,26 +21,81 @@ class OqlInterpreterTest extends ItopTestCase
|
||||
parent::setUp();
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT."core/oql/oqlinterpreter.class.inc.php");
|
||||
require_once(APPROOT."core/oql/oqlinterpreter.class.inc.php");
|
||||
require_once(APPROOT.'core/dbobject.class.php');
|
||||
require_once(APPROOT."core/dbobjectsearch.class.php");
|
||||
require_once(APPROOT."core/modelreflection.class.inc.php");
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ParseProvider
|
||||
* @param $sQuery
|
||||
*/
|
||||
public function testParse($sQuery)
|
||||
{
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery
|
||||
|
||||
$this->debug($oTrash);
|
||||
}
|
||||
|
||||
public function ParseProvider()
|
||||
{
|
||||
return array(
|
||||
array("SELECT Contact WHERE org_id->deliverymodel_id->name = 'toto'"),
|
||||
array("SELECT Contact WHERE cis_list->name = 'toto'"),
|
||||
);
|
||||
}
|
||||
|
||||
public function testShorthandExpansionCloningOption()
|
||||
{
|
||||
$sQuery = "SELECT Contact WHERE org_id->deliverymodel_id->name = 'Standard support' AND 1 AND 1 AND 1";
|
||||
|
||||
$oDbObjectSearch = \DBObjectSearch::FromOQL($sQuery);
|
||||
$oDbObjectSearchExpandedClone1 = $oDbObjectSearch->ShorthandExpansion(true);
|
||||
$oDbObjectSearchExpandedClone2 = $oDbObjectSearch->ShorthandExpansion(true);
|
||||
$oDbObjectSearchExpandedBase1 = $oDbObjectSearch->ShorthandExpansion();
|
||||
$oDbObjectSearchExpandedBase2 = $oDbObjectSearch->ShorthandExpansion();
|
||||
|
||||
$this->assertSame($oDbObjectSearchExpandedBase1, $oDbObjectSearch);
|
||||
$this->assertSame($oDbObjectSearchExpandedBase2, $oDbObjectSearch);
|
||||
|
||||
$this->assertNotSame($oDbObjectSearchExpandedClone1, $oDbObjectSearch);
|
||||
$this->assertNotSame($oDbObjectSearchExpandedClone2, $oDbObjectSearch);
|
||||
|
||||
$this->assertNotSame($oDbObjectSearchExpandedClone1, $oDbObjectSearchExpandedClone2);
|
||||
|
||||
$this->debug($oDbObjectSearch->ToOQL());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ShorthandExpansionProvider
|
||||
* @param $sQuery
|
||||
*/
|
||||
public function testShorthandExpansion($sQuery)
|
||||
{
|
||||
$oDbObjectSearch = \DBObjectSearch::FromOQL($sQuery);
|
||||
$oDbObjectSearchExpanded = $oDbObjectSearch->ShorthandExpansion();
|
||||
|
||||
|
||||
$this->assertSame($oDbObjectSearch, $oDbObjectSearchExpanded);
|
||||
|
||||
$this->debug($oDbObjectSearch->ToOQL());
|
||||
}
|
||||
|
||||
public function ShorthandExpansionProvider()
|
||||
{
|
||||
return array(
|
||||
array("SELECT Contact WHERE org_id->deliverymodel_id->name = 'tato'"),
|
||||
array('SELECT Contact WHERE cis_list->name = "Cluster1"'),
|
||||
array('SELECT Contact WHERE cis_list->name like "%m%"'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider ParseProvider
|
||||
* @param $sQuery
|
||||
*/
|
||||
public function testParse($sQuery)
|
||||
{
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery
|
||||
|
||||
$this->debug($oTrash);
|
||||
}
|
||||
|
||||
public function ParseProvider()
|
||||
{
|
||||
return array(
|
||||
array("SELECT Contact WHERE org_id->deliverymodel_id->name = 'Standard support'"),
|
||||
array("SELECT Contact WHERE cis_list->name = 'toto'"),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user