4.2.2. Работа классом EditPickerHelper

Data Source

Добавлено в версии 4.2.2.

Изображение блока "inp_edit_picker" на форме редактирования.

Изображение блока «inp_edit_picker» на форме редактирования.

Блок inp_edit_picker представляет из себя два поля в одном из которых находятся выбранные элементы, а во втором все доступные элементы за исключением уже выбранных. Данный блок позволяет выбрать несколько элементов одновременно и перенести их во второй список.

Класс EditPickerHelper автоматизирует обработку данных, полученных из блока inp_edit_picker.

Использование на шаблоне

Использовать блок inp_edit_picker в шаблоне можно следующим образом:

<inp2:m_RenderElement
    name="inp_edit_picker" prefix="sample-prefix" field="OptionField" title="la_fld_OptionField"
    optprefix="option-prefix" option_key_field="OptionId" option_value_field="OptionTitle"
    size="10" style="CSS definitions"
/>

Ниже приведено описание параметров блока inp_edit_picker. Параметры name, prefix, field, title и style являются стандартными для всех блоков, используемых на формах редактирования, поэтому в данной статье они описаны не будут.

параметр

описание параметра

optprefix (string)

Префикс, данные которого будут использованы для заполнения списка доступных и выбранных опций.

option_key_field (string)

Поле, объявленное в unit config префикса опций (указанного в optprefix), которое содержит идентификатор (ID) опции.

option_value_field (string)

Поле, объявленное в unit config префикса опций (указанного в optprefix), которое содержит название опции.

size (int)

Количество показываемых опций без боковой полосы прокрутки. По умолчанию данный параметр равен 15.

Базовая настройка

Независимо от используемого типа хранения данных, полученных из блока inp_edit_picker необходимо выполнить следующие два действия.

Прописать в конфигурационном файле префикса описание поля, которое будет использоваться для хранения данных, полученных из блока inp_edit_picker:

$config = Array(
    'Prefix' => 'sample-prefix',

    'Fields' => Array (
        'OptionField' => Array (
            'type' => 'string',
            'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %1$s FROM ' . TABLE_PREFIX . 'OptionTable',
            'option_key_field' => 'OptionId', 'option_title_field' => 'OptionTitle',
            'required' => 1, 'not_null' => 1, 'default' => 0
        ),
    ),

    'Grids' => Array (
        'Default' => Array (
            'Icons' => Array ('default' => 'icon16_custom.gif'),
            'Fields' => Array(
                'OptionField' => Array (
                    'title' => 'la_col_OptionField', 'data_block' => 'grid_picker_td',
                    'filter_block' => 'grid_picker_filter', 'header_block' => 'grid_column_title_no_sorting'
                ),
            ),
        ),
    ),
);

Учитывая форму хранения значений в поле OptionField ему не представляется возможным сделать осмысленную сортировку. В следствие чего используется блок grid_column_title_no_sorting, который убирает возможность сортировки по полю.

Примечание

В случае, когда данные будут храниться в отдельной таблице поле должно быть виртуальным.

Прописать в шаблоне редактирования тег, использующий данное поле. К данному моменту результатом использования блока inp_edit_picker будут заполненные списки доступных и выбранных опций, правда в обоих из них будет полных список опций. Для того, чтобы выбранные опции показывались правильно, нужно в обработчике событий используемого префикса опций (указанного в optprefix; в данной статье это option-prefix) переписать метод SetCustomQuery. Сутью переписывания метода является добавления фильтра по выбранным и доступным опциям:

/**
 * Applies edit picker filters
 *
 * @param kEvent $event
 */
function SetCustomQuery(&$event)
{
    $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper');
    /* @var $edit_picker_helper EditPickerHelper */

    $edit_picker_helper->applyFilter($event, 'OptionId');
}

После выполнения выше указанных действий всё должно корректно заработать. Правда для хранения данных в связанной таблице (каждое выбранное значение будет отдельной записью) требуется дополнительная настройка, описанная ниже.

Хранение данных в связанной таблице

У связанной таблицы должен быть свой конфигурационный файл и обработчик событий, так как она должна быть подчинённым префиксом относительно главного. В данной статье главный префикс это sample-prefix.

Создать конфигурационный файл подчинённого префикса, в котором кроме связки его с главным префиксом прописать hook, который будет сохранять выбранные данные.

$config = Array (
    'Prefix' => 'sample-prefix-child',
    'Hooks' => Array (
        Array (
            'Mode' => hAFTER,
            'Conditional' => false,
            'HookToPrefix' => '#PARENT#',
            'HookToSpecial' => '*',
            'HookToEvent' => Array('OnAfterItemCreate', 'OnAfterItemUpdate'),
            'DoPrefix' => '',
            'DoSpecial' => '*',
            'DoEvent' => 'OnSaveChildren',
        ),
    ),

    'ForeignKey' => 'ParentId',
    'ParentTableKey' => 'ParentId',
    'ParentPrefix' => 'parent',
    'AutoDelete' => true,
    'AutoClone' => true,

    'Fields' => Array (
        'ChildId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
        'ParentId' => Array ('type' => 'int', 'not_null' => 1, 'required' => 1, 'default' => 0),
        'OptionId' => Array ('type' => 'int', 'not_null' => 1, 'required' => 1, 'default' => 0),
    ),
);

В обработчике событий подчинённого префикса прописать тело hook:

/**
 * [HOOK] Saves changes from edit picker
 *
 * @param kEvent $event
 */
function OnSaveChildren(&$event)
{
    $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper');
    /* @var $edit_picker_helper EditPickerHelper */

    $edit_picker_helper->SaveValues($event, 'OptionField', 'OptionId');
}

В конфигурационном файле главного префикса нужно прописать его связку с подчинённым префиксом:

$config = Array (
    'SubItems' => Array('sample-prefix-child'),
);

Добавить в обработчик событий главного префикса загрузку значений в блок inp_edit_picker из подчинённой таблицы:

/**
 * Loads edit picker data
 *
 * @param kEvent $event
 */
function OnAfterItemLoad(&$event)
{
    parent::OnAfterItemLoad($event);

    $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper');
    /* @var $edit_picker_helper EditPickerHelper */

    $edit_picker_helper->LoadValues($event, 'OptionId', 'sample-prefix-child.OptionId');
}

В обработчике тегов (TagProcessor) переписать метод PrepareListElementParams, который будет использоваться для вывода данных (из тэга PrintList) из связанной таблицы в списке главного префикса:

function PrepareListElementParams(&$object, &$block_params)
{
    $edit_picker_helper =& $this->Application->recallObject('EditPickerHelper');
    /* @var $edit_picker_helper EditPickerHelper */

    $event = new kEvent($object->getPrefixSpecial() . ':OnAfterItemLoad');
    $edit_picker_helper->LoadValues($event, 'OptionField', 'sample-prefix-child.OptionId');
}

После этого, для каждой записи в списке, выбранные в поле OptionField опции будут выводиться через запятую.

Фильтрация в списке

На данный момент нету стандартного фильтра, который будет корректно осуществлять фильтрацию данных по связанной таблице. Из-за этого надо будет переписать класс kSearchHelper, который зарегистрирован в фабрике классов под pseudo SearchHelper.

Для начала нужно создать класс наследник:

class ESearchHelper extends kSearchHelper {

    function getCustomFilterSearchClause(&$object, $field_name, $filter_type, $field_options)
    {
        if ($field_name == 'OptionField' && $filter_type == 'options') {
            extract( $this->getFieldInformation($object, $field_name) );
            $field_value = strlen($field_options['submit_value']) ? $this->Conn->qstr($field_options['submit_value']) : false;
            if ($field_value) {
                $sub_sql = 'SELECT children.ParentId
                            FROM ' . $this->Application->getUnitOption('sample-prefix-child', 'TableName') . ' children
                            WHERE children.OptionId = ' . $field_value;
                $filter_value = $object->TableName . '.' . $object->IDField . ' IN (' . $sub_sql . ')';
            }

            $field_options['sql_filter_type'] = $sql_filter_type;
            $field_options['value'] = $filter_value;

            return $field_options;
        }

        return parent::getCustomFilterSearchClause($object, $field_name, $filter_type, $field_options);
    }
}

Файл будет называться e_search_helper.php (согласно правилу назначения имён) и находиться в директории custom/units/sections.

Потом нужно зарегистрировать новый класс в фабрике классов:

$config = Array (
    'RegisterClasses' => Array (
        Array ('pseudo' => 'SearchHelper', 'class' => 'ESearchHelper', 'file' => 'e_search_helper.php'),
    ),
);