N°3200 Datatable "filter list" icon : specific query for 1,n and n,n objects relations

This specific query passes attcodes list to display to UI.php, but this endpoint doesn't handle it yet. Adding this enhancements would require too much time for now (datatables legacy code), hopefully a refactoring work will be done soon and we'll get back to it !

Note that dedicated ContextTag is renamed from Search to ObjectSearch
This commit is contained in:
Pierre Goiffon
2023-01-04 15:53:32 +01:00
parent a62a373f64
commit 4f7a1ea9da
7 changed files with 123 additions and 23 deletions

View File

@@ -2223,7 +2223,7 @@ class MenuBlock extends DisplayBlock
$oActionsToolbar->AddSubBlock($oActionButton);
} else {
// - Filter list
$sSearchUrl = utils::GetDataTableSearchUrl($this->m_oFilter);
$sSearchUrl = utils::GetDataTableSearchUrl($this->m_oFilter, $aExtraParams);
if (!empty($sSearchUrl)) {
$oActionButton = ButtonUIBlockFactory::MakeIconLink(
'fas fa-filter',

View File

@@ -1111,6 +1111,7 @@ class OQLMenuNode extends MenuNode
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(

View File

@@ -1542,20 +1542,20 @@ class utils
}
/**
* We cannot use iMenuId (corresponding values in iPopupMenuExtension constants) as value is always \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT
* We cannot use iMenuId (corresponding values in {@see \iPopupMenuExtension} constants) as value is always {@see \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT}
* whenever we are in a datatable, whereas it is included in a object tab, a dashlet or a search.
*
* So a {@see \ContextTag} is set on the corresponding calls.
*
* @return bool true if we are in a search page context, either directly or by the datatable ajax call
*
* @since 3.0.0
* @since 3.1.0 N°3200
*
* @uses \ContextTag::TAG_SEARCH
* @uses \ContextTag::TAG_OBJECT_SEARCH
*/
public static function IsCurrentPageASearch(): bool
{
if (ContextTag::Check(ContextTag::TAG_SEARCH)) {
if (ContextTag::Check(ContextTag::TAG_OBJECT_SEARCH)) {
return true;
}
@@ -1564,30 +1564,113 @@ class utils
/**
* @param \DBObjectSearch $oFilter object list
* @param array $aExtraParams
*
* @return string|null null if we are already in a search, otherwise the URL to open this list in a search
*
* @throws \ArchivedObjectException
* @throws \CoreException
*
* @uses utils::IsCurrentPageASearch()
*
* @since 3.0.0
* @since 3.1.0 N°3200
*/
public static function GetDataTableSearchUrl(DBSearch $oFilter): ?string
public static function GetDataTableSearchUrl(DBSearch $oFilter, array $aExtraParams): ?string
{
if (static::IsCurrentPageASearch()) {
// we don't want to add the link when already in a search page !
return null;
}
$bIsObjectRelation = isset($aExtraParams['object_id'], $aExtraParams['target_attr']);
if ($bIsObjectRelation) {
[$oDataTableSearchFilter, $aParams] = static::GetDataTableSearchForRelations($oFilter, $aExtraParams);
} else {
$oDataTableSearchFilter = $oFilter;
$aParams = [];
}
if (isset($aExtraParams['table_id'])) {
$aParams['table_id'] = $aExtraParams['table_id'];
}
$sParams = json_encode($aParams);
$sAppRootUrl = static::GetAbsoluteUrlAppRoot();
$oAppContext = new ApplicationContext();
$sUrl = $sAppRootUrl.'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
$sUrl = $sAppRootUrl
.'pages/UI.php?operation=search&'
.$oAppContext->GetForLink()
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
return $sUrl;
}
/**
* Rewrites filter for object relations, so that in the search page we will have the correct criteria and will be able to use "configure this list"
*
* @param \DBSearch $oFilter object list
* @param array{link_attr: string, target_attr: string, object_id: string} $aExtraParams
*
* @return array{\DBObjectSearch, string[]}
* @throws \CoreException
* @throws \OQLException
*
* @since 3.1.0 N°3200
*/
private static function GetDataTableSearchForRelations(DBSearch $oFilter, array $aExtraParams): array
{
$sObjectId = $aExtraParams['object_id'];
$bIsLinkedSetIndirect = isset($aExtraParams['link_attr']);
if ($bIsLinkedSetIndirect) {
//--- AttributeLinkedSetIndirect (n,n => lnk class)
$sLnkClass = $oFilter->GetClass();
$sExtKeyToObjectClass = $aExtraParams['link_attr'];
$sExtKeyToRemoteClass = $aExtraParams['target_attr'];
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oLnkExtKeyToRemote = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToRemoteClass);
$sRemoteClass = $oLnkExtKeyToRemote->GetTargetClass();
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oLnkExtKeyToObject = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToObjectClass);
$sObjectClass = $oLnkExtKeyToObject->GetTargetClass();
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oObjectExtKeyToLnk = $oLnkExtKeyToObject->GetMirrorLinkAttribute();
$sObjectExtKeyToLnkClass = $oObjectExtKeyToLnk->GetCode();
$sRemoteClassAliasName = ormLinkSet::REMOTE_ALIAS;
$sLnkClassAliasName = ormLinkSet::LINK_ALIAS;
$sOql = <<<SQL
SELECT {$sRemoteClassAliasName},{$sLnkClassAliasName}
FROM {$sRemoteClass} AS {$sRemoteClassAliasName}
JOIN {$sLnkClass} AS {$sLnkClassAliasName} ON {$sLnkClassAliasName}.$sExtKeyToRemoteClass = {$sRemoteClassAliasName}.id
WHERE {$sLnkClassAliasName}.$sExtKeyToObjectClass = $sObjectId
SQL;
$aAttCodesToDisplay = MetaModel::GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay($sObjectClass, $sObjectExtKeyToLnkClass, $sRemoteClass, $sExtKeyToRemoteClass);
/** @noinspection PhpUnnecessaryLocalVariableInspection */
$sAttCodesToDisplay = implode(',', $aAttCodesToDisplay);
$aParams = [
'zlist' => false,
'extra_fields' => $sAttCodesToDisplay,
];
} else {
//--- AttributeLinkedSet (1,n => AttributeExternalKey)
$sClass = $oFilter->GetClass();
$sExtKeyCode = $aExtraParams['target_attr'];
$sOql = "SELECT $sClass WHERE $sExtKeyCode = $sObjectId";
$aParams = [];
}
$oDataTableSearchFilter = DBSearch::FromOQL($sOql);
return [$oDataTableSearchFilter, $aParams];
}
/**
* @param string $sEnvironment
*

View File

@@ -52,13 +52,17 @@
*/
class ContextTag
{
public const TAG_PORTAL = 'GUI:Portal';
public const TAG_CRON = 'CRON';
public const TAG_PORTAL = 'GUI:Portal';
public const TAG_CRON = 'CRON';
public const TAG_CONSOLE = 'GUI:Console';
public const TAG_SETUP = 'Setup';
public const TAG_SETUP = 'Setup';
public const TAG_SYNCHRO = 'Synchro';
public const TAG_REST = 'REST/JSON';
public const TAG_SEARCH = 'Search';
public const TAG_REST = 'REST/JSON';
/**
* @var string
* @since 3.1.0 N°3200
*/
public const TAG_OBJECT_SEARCH = 'ObjectSearch';
protected static $aStack = array();

View File

@@ -2072,6 +2072,9 @@ abstract class MetaModel
*
* @return string[] attcodes to display, containing aliases
* @throws \CoreException
*
* @since 3.0.0 N°2334 added code for n-n relations in {@see BlockIndirectLinksViewTable::GetAttCodesToDisplay}
* @since 3.1.0 N°3200 method creation so that it can be used elsewhere
*/
public static function GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay(string $sObjectClass, string $sObjectLinkedSetIndirectAttCode, string $sRemoteClass, string $sLnkExternalKeyToRemoteClassAttCode):array
{
@@ -2079,17 +2082,17 @@ abstract class MetaModel
$aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($sRemoteClass);
$aLnkAttCodesToDisplay = array_map(
function ($oLnkAttDef) {
return \ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode();
return ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode();
},
$aLnkAttDefsToDisplay
);
if (!in_array(\ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode, $aLnkAttCodesToDisplay)) {
if (!in_array(ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode, $aLnkAttCodesToDisplay)) {
// we need to display a link to the remote class instance !
$aLnkAttCodesToDisplay[] = \ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode;
$aLnkAttCodesToDisplay[] = ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode;
}
$aRemoteAttCodesToDisplay = array_map(
function ($oRemoteAttDef) {
return \ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode();
return ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode();
},
$aRemoteAttDefsToDisplay
);

View File

@@ -143,12 +143,15 @@ function SetObjectBreadCrumbEntry(DBObject $oObj, WebPage $oPage)
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true)
function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = [])
{
//search block
$oBlockForm = null;
if ($bSearchForm) {
$aParams = array('open' => $bSearchFormOpen, 'table_id' => 'result_1');
$aParams['open'] = $bSearchFormOpen;
if (false === isset($aParams['table_id'])) {
$aParams['table_id'] = 'result_1';
}
if (!empty($sBaseClass)) {
$aParams['baseClass'] = $sBaseClass;
}
@@ -519,7 +522,7 @@ try
///////////////////////////////////////////////////////////////////////////////////////////
case 'search': // Serialized DBSearch
$oSearchContext = new ContextTag(ContextTag::TAG_SEARCH);
$oSearchContext = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
$sFormat = utils::ReadParam('format', '');
$bSearchForm = utils::ReadParam('search_form', true);
@@ -530,7 +533,13 @@ try
$oP->set_title(Dict::S('UI:SearchResultsPageTitle'));
$oFilter = DBSearch::unserialize($sFilter); // TO DO : check that the filter is valid
$oFilter->UpdateContextFromUser();
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat);
//FIXME Params won't work as expected :(
// During the ajax call fetching the datatable data, the URL is rewritten and the info are lost, and we are getting a worse result :(
// $sParams = utils::ReadParam('aParams', '{}', false, \utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
// $aParams = json_decode($sParams, true);
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, ''); //, true, $aParams
break;
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,7 +29,7 @@ try
throw new AjaxSearchException("Invalid query (empty filter)", 400);
}
$oSearchContext = new ContextTag(ContextTag::TAG_SEARCH);
$oSearchContext = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
$oPage = new AjaxPage("");
$oPage->SetContentType('text/html');