За более чем 10 лет работы с Vtiger мною было реализовано множество проектов и доработок. И один из наиболее часто встречающихся запросов - это сортировка по умолчанию в фильтрах.
Vtiger предоставляет удобный инструмент создания пользовательских фильтров. Каждый пользователь может настроить под себя отображение списка модулей. Выбрать нужные колонки в таблице, отфильтровать записи. Но нельзя настроить сортировку по умолчанию.
Ниже я покажу, какие изменения следует внести в код, чтобы у вас появилась такая возможность.
Во-первых, нам нужно внести изменения в базу данных, где мы будем хранить данные сортировки по умолчанию:
alter table vtiger_customview
add sortingfield varchar(200) null;
alter table vtiger_customview
add sortingdirect varchar(10) null;
Т.е. мы добавляем два столбца в таблицу vtiger_customview: sortingfield и sortingdirect.
Затем меняем файл modules/CustomView/views/EditAjax.php, функцию process.
Добавляем строчки после данного цикла, в районе строки 107:
foreach ($customViewSharedMembers as $memberGroupLabel => $membersList) {
if(count($membersList) > 0){
$listShared = true;
break;
}
}
Добавляем:
$sortField = $customViewModel->get('sortingfield');
$sortDirect = $customViewModel->get('sortingdirect');
$viewer->assign('SORTING_FIELD',$sortField);
$viewer->assign('SORTING_DIRECT',$sortDirect);
Далее в файле layouts/v7/modules/CustomView/EditView.tpl отрисовываем непосредственно сам блок с сортировкой.
После блока, в районе строки 119:
<div>
<label class="filterHeaders">{vtranslate('LBL_CHOOSE_FILTER_CONDITIONS', $MODULE)} :</label>
<div class="filterElements well filterConditionContainer filterConditionsDiv">
{include file='AdvanceFilter.tpl'|@vtemplate_path}
</div>
</div>
добавляем следующий:
<div class="marginBottom10px">
<label class="filterHeaders">Sorting Conditions</label>
<div class="filterElements filterConditionContainer filterConditionsDiv">
<div class="row">
<div class="col-lg-6">
<select class="select2 col-lg-6" name="sortingfield">
<option value="none">{vtranslate('LBL_SELECT_FIELD',$MODULE)}</option>
{foreach key=BLOCK_LABEL item=BLOCK_FIELDS from=$RECORD_STRUCTURE}
<optgroup label='{vtranslate($BLOCK_LABEL, $SOURCE_MODULE)}'>
{foreach key=FIELD_NAME item=FIELD_MODEL from=$BLOCK_FIELDS}
{assign var=FIELD_INFO value=$FIELD_MODEL->getFieldInfo()}
{assign var=MODULE_MODEL value=$FIELD_MODEL->getModule()}
{assign var="SPECIAL_VALIDATOR" value=$FIELD_MODEL->getValidator()}
{if !empty($COLUMNNAME_API)}
{assign var=columnNameApi value=$COLUMNNAME_API}
{else}
{assign var=columnNameApi value=getCustomViewColumnName}
{/if}
<option value="{$FIELD_MODEL->$columnNameApi()}" data-fieldtype="{$FIELD_MODEL->getFieldType()}" data-field-name="{$FIELD_NAME}"
{if decode_html($FIELD_MODEL->$columnNameApi()) eq decode_html($SORTING_FIELD)}
{assign var=FIELD_TYPE value=$FIELD_MODEL->getFieldType()}
{assign var=SELECTED_FIELD_MODEL value=$FIELD_MODEL}
{if $FIELD_MODEL->getFieldDataType() == 'reference' || $FIELD_MODEL->getFieldDataType() == 'multireference'}
{$FIELD_TYPE='V'}
{/if}
{$FIELD_INFO['value'] = decode_html($SORTING_FIELD)}
selected="selected"
{/if}
{if ($MODULE_MODEL->get('name') eq 'Calendar' || $MODULE_MODEL->get('name') eq 'Events') && ($FIELD_NAME eq 'recurringtype')}
{assign var=PICKLIST_VALUES value = Calendar_Field_Model::getReccurencePicklistValues()}
{$FIELD_INFO['picklistvalues'] = $PICKLIST_VALUES}
{/if}
{if ($MODULE_MODEL->get('name') eq 'Calendar') && ($FIELD_NAME eq 'activitytype')}
{$FIELD_INFO['picklistvalues']['Task'] = vtranslate('Task', 'Calendar')}
{/if}
{if $FIELD_MODEL->getFieldDataType() eq 'reference'}
{assign var=referenceList value=$FIELD_MODEL->getWebserviceFieldObject()->getReferenceList()}
{if is_array($referenceList) && in_array('Users', $referenceList)}
{assign var=USERSLIST value=array()}
{assign var=CURRENT_USER_MODEL value = Users_Record_Model::getCurrentUserModel()}
{assign var=ACCESSIBLE_USERS value = $CURRENT_USER_MODEL->getAccessibleUsers()}
{foreach item=USER_NAME from=$ACCESSIBLE_USERS}
{$USERSLIST[$USER_NAME] = $USER_NAME}
{/foreach}
{$FIELD_INFO['picklistvalues'] = $USERSLIST}
{$FIELD_INFO['type'] = 'picklist'}
{/if}
{/if}
data-fieldinfo='{Vtiger_Util_Helper::toSafeHTML(ZEND_JSON::encode($FIELD_INFO))}'
{if !empty($SPECIAL_VALIDATOR)}data-validator='{Zend_Json::encode($SPECIAL_VALIDATOR)}'{/if}>
{if $SOURCE_MODULE neq $MODULE_MODEL->get('name')}
({vtranslate($MODULE_MODEL->get('name'), $MODULE_MODEL->get('name'))}) {vtranslate($FIELD_MODEL->get('label'), $MODULE_MODEL->get('name'))}
{else}
{vtranslate($FIELD_MODEL->get('label'), $SOURCE_MODULE)}
{/if}
</option>
{/foreach}
</optgroup>
{/foreach}
{* Required to display event fields also while adding conditions *}
{foreach key=BLOCK_LABEL item=BLOCK_FIELDS from=$EVENT_RECORD_STRUCTURE}
<optgroup label='{vtranslate($BLOCK_LABEL, 'Events')}'>
{foreach key=FIELD_NAME item=FIELD_MODEL from=$BLOCK_FIELDS}
{assign var=FIELD_INFO value=$FIELD_MODEL->getFieldInfo()}
{assign var=MODULE_MODEL value=$FIELD_MODEL->getModule()}
{if !empty($COLUMNNAME_API)}
{assign var=columnNameApi value=$COLUMNNAME_API}
{else}
{assign var=columnNameApi value=getCustomViewColumnName}
{/if}
<option value="{$FIELD_MODEL->$columnNameApi()}" data-fieldtype="{$FIELD_MODEL->getFieldType()}" data-field-name="{$FIELD_NAME}"
{if decode_html($FIELD_MODEL->$columnNameApi()) eq $SORTING_FIELD}
{assign var=FIELD_TYPE value=$FIELD_MODEL->getFieldType()}
{assign var=SELECTED_FIELD_MODEL value=$FIELD_MODEL}
{if $FIELD_MODEL->getFieldDataType() == 'reference' || $FIELD_MODEL->getFieldDataType() == 'multireference'}
{$FIELD_TYPE='V'}
{/if}
{$FIELD_INFO['value'] = decode_html($SORTING_FIELD)}
selected="selected"
{/if}
{if ($MODULE_MODEL->get('name') eq 'Calendar' || $MODULE_MODEL->get('name') eq 'Events') && ($FIELD_NAME eq 'recurringtype')}
{assign var=PICKLIST_VALUES value = Calendar_Field_Model::getReccurencePicklistValues()}
{$FIELD_INFO['picklistvalues'] = $PICKLIST_VALUES}
{/if}
{if $FIELD_MODEL->getFieldDataType() eq 'reference'}
{assign var=referenceList value=$FIELD_MODEL->getWebserviceFieldObject()->getReferenceList()}
{if is_array($referenceList) && in_array('Users', $referenceList)}
{assign var=USERSLIST value=array()}
{assign var=CURRENT_USER_MODEL value = Users_Record_Model::getCurrentUserModel()}
{assign var=ACCESSIBLE_USERS value = $CURRENT_USER_MODEL->getAccessibleUsers()}
{foreach item=USER_NAME from=$ACCESSIBLE_USERS}
{$USERSLIST[$USER_NAME] = $USER_NAME}
{/foreach}
{$FIELD_INFO['picklistvalues'] = $USERSLIST}
{$FIELD_INFO['type'] = 'picklist'}
{/if}
{/if}
data-fieldinfo='{Vtiger_Util_Helper::toSafeHTML(ZEND_JSON::encode($FIELD_INFO))}' >
{if $SOURCE_MODULE neq $MODULE_MODEL->get('name')}
({vtranslate($MODULE_MODEL->get('name'), $MODULE_MODEL->get('name'))}) {vtranslate($FIELD_MODEL->get('label'), $MODULE_MODEL->get('name'))}
{else}
{vtranslate($FIELD_MODEL->get('label'), $SOURCE_MODULE)}
{/if}
</option>
{/foreach}
</optgroup>
{/foreach}
</select>
</div>
<div class="col-lg-6">
<select class="select2 col-lg-6" name="sortingdirect">
<option value="Asc" {if $SORTING_DIRECT eq 'Asc'}selected="true"{/if}>Asc</option>
<option value="Desc" {if $SORTING_DIRECT eq 'Desc'}selected="true"{/if}>Desc</option>
</select>
</div>
</div>
</div>
</div>
Также нам необходимо сделать так, чтобы данные из формы попадали в базу.
Файл modules/CustomView/actions/Save.php в районе строки 80 перед выражением:
return $customViewModel->setData($customViewData);
Добавляем:
$sortingfield = $request->get('sortingfield');
if(!empty($sortingfield)) {
$customViewData['sortingfield'] = $sortingfield;
}
$sortingdirect = $request->get('sortingdirect');
if(!empty($sortingdirect)) {
$customViewData['sortingdirect'] = $sortingdirect;
}
И конечно же нам надо будет поправить запросы по добавлению сортировки в базу. Файл modules/CustomView/models/Record.php, функция save.
В районе строки 260, после
$status = $this->get('status');
добавляем:
$sortingfield = $this->get('sortingfield');
$sortingdirect = $this->get('sortingdirect');
Также около строки 272 меняем сам запрос на следующий:
$sql = 'INSERT INTO vtiger_customview(cvid, viewname, setdefault, setmetrics, entitytype, status, userid, sortingfield, sortingdirect) VALUES (?,?,?,?,?,?,?,?,?)';
$params = array($cvId, $viewName, $setDefault, $setMetrics, $moduleName, $status, $currentUserModel->getId(), $sortingfield, $sortingdirect);
Аналогичный трюк следует проделать и около строки 280, поменяв запрос на обновление записи:
$sql = 'UPDATE vtiger_customview SET viewname=?, setdefault=?, setmetrics=?, status=?, sortingfield=?, sortingdirect=? WHERE cvid=?';
$params = array($viewName, $setDefault, $setMetrics, $status, $sortingfield, $sortingdirect, $cvId);
Далее можно приступать к непосредственной сортировке таблицы.
Для начала создадим необходимые методы для получения сортировки. Внесём их в файл modules/CustomView/CustomView.php
function getSortFieldByCvid($cvid)
{
$cv = $this->getCustomViewByCvid($cvid);
if (!empty($cv) && isset($cv['sortingfield']) && $cv['sortingfield']) {
return $cv['sortingfield'];
} else {
return false;
}
}
function getSortDirectByCvid($cvid)
{
$cv = $this->getCustomViewByCvid($cvid);
if (!empty($cv) && isset($cv['sortingdirect']) && $cv['sortingdirect']) {
return $cv['sortingdirect'];
} else {
return false;
}
}
Чтобы они заработали, нужно внести изменения в функцию getCustomViewByCvid в том же файле, в районе строки 181. Нужно добавить две строчки:
$customviewlist["sortingfield"] = $cvrow["sortingfield"];
$customviewlist["sortingdirect"] = $cvrow["sortingdirect"];
Для успешной сортировки, нужно внести изменения в файл modules/Vtiger/models/ListView.php
Для начала создадим новые методы, внеся их в этот файл.
protected function generateDefaultOrderBy($moduleName, $pagingModel)
{
$viewid = ListViewSession::getCurrentView($moduleName);
if(empty($viewid)) {
$viewid = $pagingModel->get('viewid');
}
$customView = new CustomView($moduleName);
$sortfield = $customView->getSortFieldByCvid($viewid);
$sortdirect = $customView->getSortDirectByCvid($viewid);
if ($sortfield) {
$orderBy = $this->parseFieldName($sortfield);
$sortOrder = strtoupper($sortdirect);
return ' ORDER BY ' . $orderBy . ' ' . $sortOrder;
} else {
return ' ORDER BY vtiger_crmentity.modifiedtime DESC';
}
}
protected function parseFieldName($field)
{
$fieldArr = explode(':', $field);
return $fieldArr[0] . '.' . $fieldArr[1];
}
И изменим поведение сортировки по умолчанию, изменив функцию getListViewEntries в районе строки 249
Нужно изменить строчку
$listQuery .= ' ORDER BY vtiger_crmentity.modifiedtime DESC';
на следующую:
$listQuery .= $this->generateDefaultOrderBy($moduleName, $pagingModel);
После проведения всех этих манипуляций, в настройках фильтров у вас должен появиться рабочий раздел по пользовательской сортировки данных в таблице.