mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 23:44:11 +01:00
Compare commits
8 Commits
3.0.3-desi
...
3.0.3-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6edbf982d | ||
|
|
5aea7ccbc9 | ||
|
|
9d3e389011 | ||
|
|
46e869d1f4 | ||
|
|
064e8ee511 | ||
|
|
ca7aa482ab | ||
|
|
368b3f4ef7 | ||
|
|
dc8e6f314a |
@@ -100,15 +100,18 @@ class UILinksWidget
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
|
||||
* @param int $iUniqueId A unique identifier of new links
|
||||
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
* @param bool $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
* @param bool $bAllowRemoteExtKeyEdit If true, the ext. key to the remote object can be edited, otherwise it will be read-only
|
||||
*
|
||||
* @return array The HTML fragment of the one-row form
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false, $bAllowRemoteExtKeyEdit = true)
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
@@ -139,8 +142,11 @@ class UILinksWidget
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
|
||||
foreach ($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
|
||||
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
|
||||
|
||||
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
}
|
||||
}
|
||||
@@ -201,8 +207,11 @@ EOF
|
||||
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
|
||||
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
|
||||
|
||||
$sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
|
||||
$sValue = $oNewLinkObj->Get($sFieldCode);
|
||||
@@ -255,11 +264,24 @@ JS
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
|
||||
/**
|
||||
* @param $aRow
|
||||
* @param $sFieldCode
|
||||
* @param $aArgs
|
||||
* @param $oLnk
|
||||
* @param $oP
|
||||
* @param $sNameSuffix
|
||||
* @param $sSafeFieldId
|
||||
* @param bool $bReadOnlyField If true, the field will be read-only, otherwise it can be edited
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
|
||||
*/
|
||||
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField = false): void
|
||||
{
|
||||
if (($sFieldCode === $this->m_sExtKeyToRemote))
|
||||
{
|
||||
// current field is the lnk extkey to the remote class
|
||||
// Current field is the lnk extkey to the remote class
|
||||
$aArgs['replaceDependenciesByRemoteClassFields'] = true;
|
||||
$sRowFieldCode = 'static::key';
|
||||
$aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
|
||||
@@ -281,20 +303,31 @@ JS
|
||||
$sDisplayValue = $oLnk->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
|
||||
$aRow[$sRowFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
|
||||
.cmdbAbstractObject::GetFormElementForField(
|
||||
$oP,
|
||||
$this->m_sLinkedClass,
|
||||
$sFieldCode,
|
||||
$oAttDef,
|
||||
$sValue,
|
||||
$sDisplayValue,
|
||||
$sSafeFieldId,
|
||||
$sNameSuffix,
|
||||
0,
|
||||
$aArgs
|
||||
)
|
||||
.'</div></div></div>';
|
||||
if ($bReadOnlyField) {
|
||||
$sFieldForHtml = $sDisplayValue;
|
||||
} else {
|
||||
$sFieldForHtml = cmdbAbstractObject::GetFormElementForField(
|
||||
$oP,
|
||||
$this->m_sLinkedClass,
|
||||
$sFieldCode,
|
||||
$oAttDef,
|
||||
$sValue,
|
||||
$sDisplayValue,
|
||||
$sSafeFieldId,
|
||||
$sNameSuffix,
|
||||
0,
|
||||
$aArgs
|
||||
);
|
||||
}
|
||||
|
||||
$aRow[$sRowFieldCode] = <<<HTML
|
||||
<div class="field_container" style="border:none;">
|
||||
<div class="field_data">
|
||||
<div class="field_value">$sFieldForHtml</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
;
|
||||
}
|
||||
|
||||
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
|
||||
@@ -374,6 +407,7 @@ JS
|
||||
$oBlock->sRemoteClass = $this->m_sRemoteClass;
|
||||
|
||||
$oValue->Rewind();
|
||||
$bAllowRemoteExtKeyEdit = $oValue->Count() <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
|
||||
$aForm = array();
|
||||
$iMaxAddedId = 0;
|
||||
$iAddedId = -1; // Unique id for new links
|
||||
@@ -398,7 +432,7 @@ JS
|
||||
}
|
||||
|
||||
$iMaxAddedId = max($iMaxAddedId, $key);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bAllowRemoteExtKeyEdit);
|
||||
}
|
||||
$oBlock->iMaxAddedId = (int) $iMaxAddedId;
|
||||
|
||||
@@ -571,10 +605,11 @@ JS
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
|
||||
$iAdditionId = $iMaxAddedId + 1;
|
||||
$bAllowRemoteExtKeyEdit = count($aLinkedObjectIds) <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
|
||||
foreach ($aLinkedObjectIds as $iObjectId) {
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj)) {
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
|
||||
$aData = [];
|
||||
foreach ($aRow as $item) {
|
||||
$aData[] = $item;
|
||||
|
||||
@@ -480,6 +480,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'link_set_max_edit_ext_key' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items in the link that allow editing the remote external key. Above that limit, remote external key cannot be edited. Mind that setting this limit too high can have a negative impact on performances.',
|
||||
'default' => 50,
|
||||
'value' => 50,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'tag_set_item_separator' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Tag set from string: tag label separator',
|
||||
|
||||
@@ -1103,22 +1103,39 @@ class DBObjectSearch extends DBSearch
|
||||
public function Filter($sClassAlias, DBSearch $oFilter)
|
||||
{
|
||||
// If the conditions are the correct ones for Intersect
|
||||
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass()))
|
||||
{
|
||||
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(), $this->GetFirstJoinedClass())) {
|
||||
return $this->Intersect($oFilter);
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oFilteredSearch */
|
||||
$oFilteredSearch = $this->DeepClone();
|
||||
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oFilter, $this->m_aClasses);
|
||||
if ($oFilterExpression === false)
|
||||
{
|
||||
throw new CoreException("Limitation: cannot filter search");
|
||||
if ($oFilter instanceof DBUnionSearch) {
|
||||
$aFilters = $oFilter->GetSearches();
|
||||
} else {
|
||||
$aFilters = [$oFilter];
|
||||
}
|
||||
|
||||
$oFilteredSearch->AddConditionExpression($oFilterExpression);
|
||||
$aSearches = [];
|
||||
foreach ($aFilters as $oRightFilter) {
|
||||
/** @var \DBObjectSearch $oFilteredSearch */
|
||||
$oFilteredSearch = $this->DeepClone();
|
||||
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oRightFilter, $this->m_aClasses);
|
||||
if ($oFilterExpression === false) {
|
||||
throw new CoreException("Limitation: cannot filter search");
|
||||
}
|
||||
|
||||
return $oFilteredSearch;
|
||||
$oFilteredSearch->AddConditionExpression($oFilterExpression);
|
||||
$aSearches[] = $oFilteredSearch;
|
||||
}
|
||||
|
||||
if (count($aSearches) == 0) {
|
||||
throw new CoreException('Filtering '.$this->ToOQL().' by '.$oFilter->ToOQL().' failed');
|
||||
}
|
||||
|
||||
if (count($aSearches) == 1) {
|
||||
// return a DBObjectSearch
|
||||
return $aSearches[0];
|
||||
}
|
||||
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1184,22 +1201,10 @@ class DBObjectSearch extends DBSearch
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function Intersect(DBSearch $oFilter)
|
||||
{
|
||||
return $this->IntersectSubClass($oFilter, $this->m_aClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBSearch $oFilter
|
||||
* @param array $aRootClasses classes of the root search (for aliases)
|
||||
*
|
||||
* @return \DBUnionSearch|mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function IntersectSubClass(DBSearch $oFilter, $aRootClasses)
|
||||
{
|
||||
if ($oFilter instanceof DBUnionSearch)
|
||||
{
|
||||
// Develop!
|
||||
// Develop!
|
||||
$aFilters = $oFilter->GetSearches();
|
||||
}
|
||||
else
|
||||
@@ -1210,56 +1215,61 @@ class DBObjectSearch extends DBSearch
|
||||
$aSearches = array();
|
||||
foreach ($aFilters as $oRightFilter)
|
||||
{
|
||||
// Limitation: the queried class must be the first declared class
|
||||
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias())
|
||||
{
|
||||
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oLeftFilter */
|
||||
$oLeftFilter = $this->DeepClone();
|
||||
$oRightFilter = $oRightFilter->DeepClone();
|
||||
|
||||
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
|
||||
if ($bAllowAllData)
|
||||
{
|
||||
$oLeftFilter->AllowAllData();
|
||||
}
|
||||
|
||||
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
|
||||
{
|
||||
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
|
||||
{
|
||||
// Specialize $oLeftFilter
|
||||
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
|
||||
}
|
||||
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
|
||||
{
|
||||
// Specialize $oRightFilter
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
|
||||
}
|
||||
}
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
|
||||
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
|
||||
$aSearches[] = $oLeftFilter;
|
||||
$aSearches[] = $this->IntersectSubClass($oRightFilter, $this->m_aClasses);
|
||||
}
|
||||
|
||||
if (count($aSearches) == 1)
|
||||
{
|
||||
// return a DBObjectSearch
|
||||
return $aSearches[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DBUnionSearch($aSearches);
|
||||
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObjectSearch $oRightFilter
|
||||
* @param array $aRootClasses classes of the root search (for aliases)
|
||||
*
|
||||
* @return \DBObjectSearch
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function IntersectSubClass(DBObjectSearch $oRightFilter, array $aRootClasses): DBObjectSearch
|
||||
{
|
||||
// Limitation: the queried class must be the first declared class
|
||||
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias()) {
|
||||
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oLeftFilter */
|
||||
$oLeftFilter = $this->DeepClone();
|
||||
/** @var DBObjectSearch $oRightFilter */
|
||||
$oRightFilter = $oRightFilter->DeepClone();
|
||||
|
||||
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
|
||||
if ($bAllowAllData) {
|
||||
$oLeftFilter->AllowAllData();
|
||||
}
|
||||
|
||||
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass()) {
|
||||
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass())) {
|
||||
// Specialize $oLeftFilter
|
||||
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
|
||||
} elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass())) {
|
||||
// Specialize $oRightFilter
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
|
||||
} else {
|
||||
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
|
||||
}
|
||||
}
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
|
||||
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
|
||||
|
||||
return $oLeftFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,7 +61,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
this.RemoveSelected = function () {
|
||||
let my_id = '#'+me.id;
|
||||
$('#linkedset_'+me.id+' .selection:checked').closest('tr').each(function () {
|
||||
$('#datatable_'+me.id).DataTable().row($(this)).remove().draw();
|
||||
$('#datatable_'+me.id).DataTable().row($(this)).remove();
|
||||
var oCheckbox = $(this).find('.selection');
|
||||
let iLink = $(oCheckbox).attr('data-link-id');
|
||||
if (iLink > 0) {
|
||||
@@ -80,6 +80,9 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
me.aAdded[iUniqueId] = null;
|
||||
}
|
||||
});
|
||||
// N°6124 Only draw table once for performance reasons
|
||||
$('#datatable_'+me.id).DataTable().draw();
|
||||
|
||||
// Disable the button since all the selected items have been removed
|
||||
$(my_id+'_btnRemove').prop('disabled', true);
|
||||
|
||||
@@ -251,9 +254,10 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
function (data) {
|
||||
if (data != '') {
|
||||
$.each(data.data, function (idx, row) {
|
||||
$('#datatable_'+me.id).DataTable().row.add(row).draw();
|
||||
$('#datatable_'+me.id).DataTable().row.add(row);
|
||||
});
|
||||
|
||||
// N°6124 Only draw table once for performance reasons
|
||||
$('#datatable_'+me.id).DataTable().draw();
|
||||
|
||||
$.each(data.scripts, function (idx, script) {
|
||||
$.globalEval(script);
|
||||
@@ -263,7 +267,6 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
$(this).trigger('validate', '');
|
||||
}); // Validate newly added form fields...
|
||||
$('#datatable_'+me.id).DataTable().columns.adjust().draw();
|
||||
//$('#datatable_team_list').DataTable().columns.adjust().draw();
|
||||
$('#busy_'+me.iInputId).html('');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
|
||||
@@ -76,7 +75,8 @@ class AttributeDefinitionTest extends ItopDataTestCase {
|
||||
\$ormLinkset = \$oObject->Get('workorders_list');
|
||||
\$ormLinkset->AddItem(MetaModel::NewObject('WorkOrder', []));
|
||||
\$oObject->Set('workorders_list', \$ormLinkset);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -88,7 +88,8 @@ PHP,
|
||||
\$ormLinkset = \$oObject->Get('contacts_list');
|
||||
\$ormLinkset->AddItem(MetaModel::NewObject('lnkContactToTicket', []));
|
||||
\$oObject->Set('contacts_list', \$ormLinkset);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -97,7 +98,8 @@ PHP,
|
||||
'value',
|
||||
<<<PHP
|
||||
\$oObject->Set('value', 100);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -106,7 +108,8 @@ PHP,
|
||||
'speed',
|
||||
<<<PHP
|
||||
\$oObject->Set('speed', 1024.5);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -115,7 +118,8 @@ PHP,
|
||||
'title',
|
||||
<<<PHP
|
||||
\$oObject->Set('title', 'Some title');
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -124,7 +128,8 @@ PHP,
|
||||
'item_id',
|
||||
<<<PHP
|
||||
\$oObject->Set('item_id', 12);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -133,7 +138,8 @@ PHP,
|
||||
'org_id',
|
||||
<<<PHP
|
||||
\$oObject->Set('org_id', 3);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -142,7 +148,8 @@ PHP,
|
||||
'file',
|
||||
<<<PHP
|
||||
\$oObject->Set('file', new ormDocument('something', 'text/plain', 'something.txt'));
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
@@ -167,7 +174,8 @@ PHP,
|
||||
/** @var \ormPassword \$ormPassword */
|
||||
\$ormPassword = new ormPassword('somehash', 'somesalt');
|
||||
\$oObject->Set('password', \$ormPassword);
|
||||
PHP,
|
||||
PHP
|
||||
,
|
||||
false,
|
||||
true,
|
||||
],
|
||||
|
||||
116
tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php
Normal file
116
tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class DBUnionSearchTest extends ItopDataTestCase
|
||||
{
|
||||
|
||||
public function testFilterOnFirstSelectedClass()
|
||||
{
|
||||
$sSourceOQL = 'SELECT `Person`, `Location` FROM Person AS `Person` JOIN Location AS `Location` ON `Person`.location_id = `Location`.id WHERE (`Location`.`id` = 1)';
|
||||
$oSearch = DBSearch::FromOQL($sSourceOQL);
|
||||
|
||||
$sFilterOQL = 'SELECT `Person` FROM Person AS `Person` WHERE (`Person`.`id` = 2) UNION SELECT `Person` FROM Person AS `Person` WHERE (`Person`.`id` = 3)';
|
||||
$oVisibleObjects = DBSearch::FromOQL($sFilterOQL);
|
||||
$sClassAlias = 'Person';
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
|
||||
$this->InvokeNonPublicMethod(get_class($oSearch), 'SetDataFiltered', $oSearch, []);
|
||||
|
||||
$this->debug($oSearch->ToOQL());
|
||||
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$oSet->Count();
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignored test (provokes PHP Error)
|
||||
*
|
||||
* @dataProvider FilterOnSecondSelectedClassProvider
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testFilterOnSecondSelectedClass($sSourceOQL, $sClassAlias, $sFilterOQL, $sExpected)
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL($sSourceOQL);
|
||||
|
||||
$oVisibleObjects = DBSearch::FromOQL($sFilterOQL);
|
||||
$oVisibleObjects->AllowAllData();
|
||||
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
|
||||
|
||||
$sResult = $oSearch->ToOQL();
|
||||
$this->debug($sResult);
|
||||
|
||||
$this->assertEquals($sExpected, $sResult);
|
||||
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$oSet->CountWithLimit(1);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function FilterOnSecondSelectedClassProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'sSourceOQL' => "SELECT P1, L1 FROM Person AS P1 JOIN Location AS L1 ON P1.location_id = L1.id WHERE L1.id = 1",
|
||||
'sClassAlias' => "L1",
|
||||
'sFilterOQL' => "SELECT Location AS L2 WHERE L2.id = 2 UNION SELECT Location AS L3 WHERE L3.id = 3",
|
||||
'sExpected' => "SELECT `P1`, `L1` FROM Person AS `P1` JOIN Location AS `L1` ON `P1`.location_id = `L1`.id WHERE ((`L1`.`id` = 1) AND (`L1`.`id` = 2)) UNION SELECT `P1`, `L1` FROM Person AS `P1` JOIN Location AS `L1` ON `P1`.location_id = `L1`.id WHERE ((`L1`.`id` = 1) AND (`L1`.`id` = 3))",
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => 'SELECT L1, P1 FROM Person AS P1 JOIN Location AS L1 ON P1.location_id = L1.id WHERE L1.id = 1',
|
||||
'sClassAlias' => 'L1',
|
||||
'sFilterOQL' => 'SELECT Location AS L2 WHERE L2.id = 2 UNION SELECT Location AS L3 WHERE L3.id = 3',
|
||||
'sExpected' => 'SELECT `L1`, `P1` FROM Person AS `P1` JOIN Location AS `L1` ON `P1`.location_id = `L1`.id WHERE ((`L1`.`id` = 1) AND (`L1`.`id` = 2)) UNION SELECT `L1`, `P1` FROM Person AS `P1` JOIN Location AS `L1` ON `P1`.location_id = `L1`.id WHERE ((`L1`.`id` = 1) AND (`L1`.`id` = 3))',
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => "SELECT L1,O1 FROM Location AS L1 JOIN Organization AS O1 ON L1.org_id=O1.id JOIN Organization AS O2 ON O1.parent_id = O2.id WHERE L1.name != 'l1-name' AND O1.name != 'o1-name' AND ISNULL(O2.name) != 0",
|
||||
'sClassAlias' => "O1",
|
||||
'sFilterOQL' => "SELECT Organization AS O1 WHERE O1.id = 2 UNION SELECT Organization AS O2 WHERE O2.id = 3",
|
||||
'sExpected' => "SELECT `L1`, `O1` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O1`.`id` = 2)) UNION SELECT `L1`, `O1` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O1`.`id` = 3))",
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => "SELECT L1,O2 FROM Location AS L1 JOIN Organization AS O1 ON L1.org_id=O1.id JOIN Organization AS O2 ON O1.parent_id = O2.id WHERE L1.name != 'l1-name' AND O1.name != 'o1-name' AND ISNULL(O2.name) != 0",
|
||||
'sClassAlias' => 'O2',
|
||||
'sFilterOQL' => 'SELECT Organization AS O1 WHERE O1.id = 2 UNION SELECT Organization AS O2 WHERE O2.id = 3',
|
||||
'sExpected' => "SELECT `L1`, `O2` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O2`.`id` = 2)) UNION SELECT `L1`, `O2` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O2`.`id` = 3))",
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => "SELECT L1,O1 FROM Location AS L1 JOIN Organization AS O1 ON L1.org_id=O1.id JOIN Organization AS O2 ON O1.parent_id = O2.id WHERE L1.name != 'l1-name' AND O1.name != 'o1-name' AND ISNULL(O2.name) != 0",
|
||||
'sClassAlias' => 'O2',
|
||||
'sFilterOQL' => 'SELECT Organization AS O1 WHERE O1.id = 2 UNION SELECT Organization AS O2 WHERE O2.id = 3',
|
||||
// This is another problem, we should not be able to filter on not selected classes
|
||||
'sExpected' => "SELECT `L1`, `O1` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O2`.`id` = 2)) UNION SELECT `L1`, `O1` FROM Location AS `L1` JOIN Organization AS `O1` ON `L1`.org_id = `O1`.id JOIN Organization AS `O2` ON `O1`.parent_id = `O2`.id WHERE ((((`L1`.`name` != 'l1-name') AND (`O1`.`name` != 'o1-name')) AND (ISNULL(`O2`.`name`) != 0)) AND (`O2`.`id` = 3))",
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => "SELECT P1, O1 FROM Person AS P1 JOIN Organization AS O1 ON P1.org_id = O1.id JOIN Location AS L1 ON P1.location_id = L1.id JOIN Organization AS O2 ON L1.org_id = O2.id WHERE L1.name != '' AND P1.name != '' AND O1.name != '' AND O2.name != ''",
|
||||
'sClassAlias' => 'O1',
|
||||
'sFilterOQL' => 'SELECT Organization AS O1 WHERE O1.id = 2 UNION SELECT Organization AS O2 WHERE O2.id = 3',
|
||||
'sExpected' => "SELECT `P1`, `O1` FROM Person AS `P1` JOIN Organization AS `O1` ON `P1`.org_id = `O1`.id JOIN Location AS `L1` ON `P1`.location_id = `L1`.id JOIN Organization AS `O2` ON `L1`.org_id = `O2`.id WHERE (((((`L1`.`name` != '') AND (`P1`.`name` != '')) AND (`O1`.`name` != '')) AND (`O2`.`name` != '')) AND (`O1`.`id` = 2)) UNION SELECT `P1`, `O1` FROM Person AS `P1` JOIN Organization AS `O1` ON `P1`.org_id = `O1`.id JOIN Location AS `L1` ON `P1`.location_id = `L1`.id JOIN Organization AS `O2` ON `L1`.org_id = `O2`.id WHERE (((((`L1`.`name` != '') AND (`P1`.`name` != '')) AND (`O1`.`name` != '')) AND (`O2`.`name` != '')) AND (`O1`.`id` = 3))",
|
||||
],
|
||||
[
|
||||
'sSourceOQL' => "SELECT P1, O2 FROM Person AS P1 JOIN Organization AS O1 ON P1.org_id = O1.id JOIN Location AS L1 ON P1.location_id = L1.id JOIN Organization AS O2 ON L1.org_id = O2.id WHERE L1.name != '' AND P1.name != '' AND O1.name != '' AND O2.name != ''",
|
||||
'sClassAlias' => 'O2',
|
||||
'sFilterOQL' => 'SELECT Organization AS O1 WHERE O1.id = 2 UNION SELECT Organization AS O2 WHERE O2.id = 3',
|
||||
'sExpected' => "SELECT `P1`, `O2` FROM Person AS `P1` JOIN Organization AS `O1` ON `P1`.org_id = `O1`.id JOIN Location AS `L1` ON `P1`.location_id = `L1`.id JOIN Organization AS `O2` ON `L1`.org_id = `O2`.id WHERE (((((`L1`.`name` != '') AND (`P1`.`name` != '')) AND (`O1`.`name` != '')) AND (`O2`.`name` != '')) AND (`O2`.`id` = 2)) UNION SELECT `P1`, `O2` FROM Person AS `P1` JOIN Organization AS `O1` ON `P1`.org_id = `O1`.id JOIN Location AS `L1` ON `P1`.location_id = `L1`.id JOIN Organization AS `O2` ON `L1`.org_id = `O2`.id WHERE (((((`L1`.`name` != '') AND (`P1`.`name` != '')) AND (`O1`.`name` != '')) AND (`O2`.`name` != '')) AND (`O2`.`id` = 3))",
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user