Алексей Игошин

Советы битриксоидам: Пользовательские типы полей в Битрикс

Мы завели собственную базу знаний — Confluence. Пока что наполняем, но кое-какие полезные советы для программистов уже есть. Привожу кусок личного опыта, пользуйтесь.

Общий смысл: для нового типа переменной создаем класс, в котором описываем поведение этого типа.

  1. В init.php подключаем файл с классом и добавляем обработчик события для типа переменной.
  2. Накидываем класс типа переменной. После того как будет готов метод GetUserTypeDescription, можно уже определять переменную нашего типа (01.gif). В этой функции нужно задать базовый тип переменной (int, str, или что то еще) [1]. В моем случае, все данные хранились в других инфоблоках, поэтому тип мог быть любой.
  3. Функции описаны в классе. Основные, которыми пришлось пользоваться: GetPropertyFieldHtml и ConvertToDB — получение данных для вывода и сохраниение измененных. В GetPropertyFieldHtml происходит вывод html-кода, который увидит пользователь при редактировании.

Пара особенностей:

  • В эти функции не приходит ID инфоэлемента. Поэтому он получается через $_REQUEST.
  • html-код выводится в определяемой битриксом ячейке таблицы (02.gif), поэтому возможности оформления админки несколько ограничены.
  • Свое поле должно иметь нужный вид, чтобы мы смогли получить его впоследствие для сохранения в функции ConvertToDB. Поэтому называем его "PROP[{$propertyID}][VALUE]», где $propertyID — ID переменной. Разумеется, нам надо больше одного значения. Поэтому используем массивы «PROP[{$propertyID}][VALUE][{$idx}][PRICE]» и вообще что угодно — придет нормально.

Полезные ссылко:

Функция, описывающая поведение пользовательского свойства и его базовый тип.

Возможности класса. Это больше, чем просто вывод данных и сохранение.

Код init.php:


Код класса типа переменной (самое главное на этой странице):

 
    
SetAdditionalCSS(P_CSS . 'admin.css');
        $APPLICATION->AddHeadScript(P_JS . 'plugins/jquery-1.7.1.min.js');
        $APPLICATION->AddHeadScript(P_JS . 'plugins/jquerymx-3.2.custom.min.js');
        $APPLICATION->AddHeadScript(P_JS . 'tools.js');
        $APPLICATION->AddHeadScript(P_JS . 'admin/tableprice.js');
    }

    /*
     * GetUserTypeDescription
     * Функция возвращает массив описывающий поведение 
пользовательского свойства
     *
     * @return array массив описывающий поведение пользовательского свойства
     */
    function GetUserTypeDescription() {
 
        return array(
            'PROPERTY_TYPE'         => 'S',
            'USER_TYPE'             => 'Bedprice',
            'DESCRIPTION'           => 'Цена кровати',
            'GetAdminListViewHTML'  => array('CIBlockPropertyBedprice', 
'GetAdminListViewHTML'),
            'GetPropertyFieldHtml'  => array('CIBlockPropertyBedprice', 
'GetPropertyFieldHtml'),
            'ConvertToDB'           => array('CIBlockPropertyBedprice', 
'ConvertToDB')
        );
    }
      
    /*
     * GetAdminListViewHTML
     * Показ в списке
     *
     * @param array $arProperty Метаданные свойства
     * @param array $value      Значение свойства
     * @param array $strHTMLControlName Имена элементов управления 
для заполнения значения свойства и его описания
     *
     * @return string строка html
     */
    function GetAdminListViewHTML($arProperty, $value, $strHTMLControlName) {
 
        return 'Цена кровати';
    }
    
     /*
     * GetPropertyFieldHtml
     * Отображение в форме редактирования
     *
     * @param array $arProperty Метаданные свойства
     * @param array $value      Значение свойства
     * @param array $strHTMLControlName Имена элементов управления 
для заполнения значения свойства и его описания
     *
     * @return string строка html
     */
    function GetPropertyFieldHtml($arProperty, $value, $strHTMLControlName) {
 
        $prodId = $_REQUEST['ID'];
        $iblockId = IB_PROD_PRICE_BED;
        if (empty($prodId)) {
         
            return;
        }
          
        // получаем ширины и материалы оснований кроватей
        $bedWidthElements = getBedWidthElements();
        $colorElements = getColorElements(array(), 
array('SECTION_ID' => IB_CATALOG_CAT_BASE_MATHERIALS));
 
         // получаем заполненные материалы и ширины
        $priceElements = getProdPriceBedElements(array(), array('PROPERTY_PROD' => 
$prodId));
 
        if (count($priceElements) > 0) {
         
            $matherials = array();
            $widths = array();
 
            foreach ($priceElements as $priceElement) {
             
                $matherials[]   = $priceElement['PROPERTY_COLOR_VALUE'];
                $widths[]       = $priceElement['PROPERTY_BEDWIDTH_VALUE'];
            }
 
            $matherials = array_unique($matherials);
            $widths = array_unique($widths);
        }
         
         // вывод
        $params = array(
            'iblockId'      => $iblockId,
            'prices'        => $priceElements,
            'rows'          => $matherials,
            'cols'          => $widths,
            'rowElements'   => $colorElements,
            'colElements'   => $bedWidthElements
        );
         
        self::addHeaders();
        // выводим html-код
        CIBlockPropertyTablepriceEditHtml($params, $arProperty);
 
        return;
    }
 
    /*
     * ConvertToDB
     * Сохранение в БД
     *
     * @param array $arProperty Метаданные свойства
     * @param array $value      Значение свойства
     *
     * @return string пустая строка
     */
    function ConvertToDB($arProperty, $value) {
 
        // получение полей и их обработка
        global $USER;
        $prodId = $_REQUEST['ID'];
        $fieldId = $arProperty['ID'];
        $iblockId = IB_PROD_PRICE_BED;
        $values = $value['VALUE'];
 
        if (intval($prodId) <= 0 || intval($iblockId) <= 0) 
        { // просто валим отсюда
 
            return '';
        }
 
         // удаляем старые поля товара
        $oldElements = getProdPriceBedElements(array(), 
array('PROPERTY_PROD' => $prodId));
        if (count($oldElements)) {
 
            foreach ($oldElements as $oldElement) {
 
                $deleteId = $oldElement['ID'];
                CIBlockElement::Delete($deleteId);
            }
        }
 
         // сохраняем пришедшие поля
        $el = new CIBlockElement;
        foreach ($values as $keyi => $valArr) {
 
            foreach ($valArr as $keyj => $val) {
             
                if (!isset($val)) {
                 
                    continue;
                }
                 
              
                $params = array(
                    'COLOR'     => $keyi,
                    'BEDWIDTH'  => $keyj,
                    'PROD'      => $prodId,
                    'PRICE'     => $val
                );
             
                $arAddArray = array(
                    'MODIFIED_BY'       => $USER->GetID(),
                    'IBLOCK_SECTION_ID' => false,
                    'IBLOCK_ID'         => $iblockId,
                    'PROPERTY_VALUES'   => $params,
                    'NAME'              => 'Элемент',
                    'ACTIVE'            => 'Y'
                );
             
                $el->Add($arAddArray);
            }
        }
 
        return '';
    }
}

Код html-шаблона (вдруг пригодится):

<? if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) { die(); }
 
/* * CIBlockPropertyTablepriceEditHtml * Вывод редактирования цен кровати * * @param array $params разные параметры * @param array $arProperty параметры битрикса * * @return */
function CIBlockPropertyTablepriceEditHtml($params, $arProperty) {
 
    $colElements = $params['colElements'];
    $rowElements = $params['rowElements'];
    $iblockId = $params['iblockId'];
 
    if ($iblockId == IB_PROD_PRICE_BED) {
 
        $rowParam = 'COLOR'; 
        $colParam = 'BEDWIDTH'; 
    } else {
 
        $rowParam = 'SIZE'; 
        $colParam = 'TEXTILE'; 
    }
 
    if (count($rowElements) <= 0 || count($colElements) <= 0) {
 
        if ($iblockId == IB_PROD_PRICE_BED) { ?>
            <p>Не заданы ширины кроватей или материалы основания.</p>
        <? } else { ?>
            <p>Не заданы размеры диванов или категории ткани.</p>
        <? }
 
        return false;
    }
 
    $namePref = 'PROP[' . $arProperty['ID'] . '][VALUE]';
    ?>
    <div class="tablepriceEdit js-tablepriceEdit">
        <div class="editTextareaHolder">
            <textarea name="editTextarea" class="editTextarea"></textarea>
        </div>
        <table class="adminInpTbl adminEditTbl">
            <tbody>
                <tr>
                    <th></th>
                    <? foreach ($colElements as $colElement) { ?>
                        <th colId="<?=$colElement['ID'];?>">
                            <div class="tblSpacer"><?=$colElement['NAME'];?>
</div>
                        </th>
                    <? } ?>
                </tr>
                <? foreach ($rowElements as $rowElement) {
 
                    $rowId = $rowElement['ID'];
                    ?>
                    <tr rowId="<?=$rowId;?>">
                        <td rowId="<?=$rowId;?>"><?=$rowElement['NAME'];?></td>
                        <? foreach ($colElements as $colElement) {
 
                            $colId = $colElement['ID'];
                            $hiddenName = $namePref . '[' . $rowId . '][' 
. $colId . ']';
                            ?>
                            <td rowId="<?=$rowId;?>" colId="<?=$colId;?>" 
hiddenName="<?=$hiddenName;?>">
                                <div class="tblSpacer">
                                    <? $val = getPriceByParams($params['prices'], 
$rowId, $colId, $rowParam, $colParam); ?>
                                    <? if (isset($val)) { ?>
                                        <?=$val;?>
                                        <input type='hidden' value='<?=$val;?>' 
name="<?=$hiddenName;?>">
                                    <? } ?>
                                </div>
                            </td>
                        <? } ?>
                    </tr>
                <? } ?>
            </tbody>
        </table>
        <div>&nbsp;</div>
        <? 
        $commonClassname = 'js-dataSaver dataSaver_' . $arProperty['ID'] . '_';
        if ($iblockId == IB_PROD_PRICE_BED) { ?>
 
<a href="javascript:void(0);" class="js-rowElements 
<?=($commonClassname . IB_COLOR);?>"
                proptype="<?=IB_COLOR;?>" fieldId="<?=$arProperty['ID'];?>"
                ids="<?=implode('_', $params['rows']);?>">Материалы</a>
            &nbsp;&nbsp;&nbsp;&nbsp;
 
<a href="javascript:void(0);" class="js-colElements 
<?=($commonClassname . IB_BED_WIDTH);?>"
                proptype="<?=IB_BED_WIDTH;?>" fieldId="<?=$arProperty['ID'];?>"
                ids="<?=implode('_', $params['cols']);?>">Варианты ширины</a>
        <? } else { ?>
 
<a href="javascript:void(0);" class="js-rowElements 
<?=($commonClassname . IB_SIZES_SOFA);?>"
                proptype="<?=IB_SIZES_SOFA;?>" fieldId="<?=$arProperty['ID'];?>"
                ids="<?=implode('_', $params['rows']);?>">Размеры изделий</a>
            &nbsp;&nbsp;&nbsp;&nbsp;
 
<a href="javascript:void(0);" class="js-colElements 
<?=($commonClassname . IB_TEXTILE);?>"
                proptype="<?=IB_TEXTILE;?>" 
                param="<?=$params['factoryId'];?>" 
                fieldId="<?=$arProperty['ID'];?>"
                ids="<?=implode('_', $params['cols']);?>">Категории тканей</a>
        <? } ?>
    </div>
<?
}

И немного картинок (потому что я не люблю читать без картинок).

Как оно выглядит при выборе типа переменной:

Как оно выглядит при редактировании:


 

Что еще почитать по этой теме

Интеграция с 1C — готовьте напильник
Сибирикс
05 Мая 2016
Интеграция с 1C —  готовьте напильник
Интеграция не из коробки — штука сложная. Чтобы её сделать, нужны адекватные специалисты, терпение, хитрость и силовые приёмы.
Обновление: Доска задач теперь прогнозирует будущее
Сибирикс
18 Февраля 2016
Обновление: Доска задач теперь прогнозирует будущее
Scrumban теперь может рассчитать, когда закончится спринт, если вы будете продолжать в том же духе.
Традиционный безалкогольный хакатон в Сибириксе: пишем бесплатный HelpDesk
Владимир Завертайлов
24 Ноября 2015
Традиционный безалкогольный хакатон в Сибириксе: пишем бесплатный HelpDesk

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

У вас есть проект?

Давайте обсудим его. Продумаем. И сделаем!

Заказать клёвый проект
Заявка отправлена
Спасибо, ваша заявка отправлена. Эксперт студии Сибирикс свяжется с вами в ближайшее время для уточнения подробностей.