База знаний

Пример добавления кастомного типа для пользовательских полей

bitrix

Пример добавления кастомного типа данных для пользовательских полей


Добавление типа "Привязка к элементам инфоблока с автодополнением" на основе свойства с одноименный типом.

Ссылки на документацию

https://dev.1c-bitrix.ru/api_d7/bitrix/main/userfield/uf-fieldclass.php

Расположение базовых типов

Расположение файл свойства у которого берем функционал

Подключение класса в init.php

Bitrix\Main\Loader::registerAutoLoadClasses(null, [
    'lib\UserType\CUserTypeElementAutocomplete' => '/local/php_interface/lib/UserType/CUserTypeElementAutocomplete.php'
]);

AddEventHandler("main", "OnUserTypeBuildList", ["lib\UserType\CUserTypeElementAutocomplete", "GetUserTypeDescription"]);

Содержимое файла CUserTypeElementAutocomplete.php

<?php
namespace lib\UserType;

use Bitrix\Main\Application;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Type;
use Bitrix\Main\UserField\Types\EnumType;
use Bitrix\Iblock;
use CDBResult;
use CUserTypeManager;
use CIBlockElementEnum;

/**
 * Class CUserTypeElementAutocomplete
 * @package Bitrix\Main\UserField\Types
 */
class CUserTypeElementAutocomplete extends EnumType
{
    public const
        USER_TYPE_ID = 'iblock_element_autocomplete',
        RENDER_COMPONENT = 'bitrix:iblock.field.element',
        MODE_EDIT_FORM = 'main.edit_form';

    protected static ?bool $iblockIncluded = null;

    /**
     * @return array
     */
    public static function getDescription(): array
    {
        return [
            'DESCRIPTION' => 'Привязка к элементам с автозаполнением',
            'BASE_TYPE' => CUserTypeManager::BASE_TYPE_INT,
        ];
    }

    /**
     * @param array $userField
     * @return array
     */
    public static function prepareSettings(array $userField): array
    {
        $height = (int)($userField['SETTINGS']['LIST_HEIGHT'] ?? 1);
        $disp = ($userField['SETTINGS']['DISPLAY'] ?? '');

        if ($disp !== static::DISPLAY_CHECKBOX && $disp !== static::DISPLAY_LIST)
        {
            $disp = static::DISPLAY_LIST;
        }

        $iblockId = (int)($userField['SETTINGS']['IBLOCK_ID'] ?? 0);

        if($iblockId <= 0)
        {
            $iblockId = '';
        }

        $elementId = (int)($userField['SETTINGS']['DEFAULT_VALUE'] ?? 0);

        if($elementId <= 0)
        {
            $elementId = '';
        }

        $activeFilter = (($userField['SETTINGS']['ACTIVE_FILTER'] ?? '') === 'Y' ? 'Y' : 'N');

        return [
            'DISPLAY' => $disp,
            'LIST_HEIGHT' => (max($height, 1)),
            'IBLOCK_ID' => $iblockId,
            'DEFAULT_VALUE' => $elementId,
            'ACTIVE_FILTER' => $activeFilter,
        ];
    }

    /**
     * @param array $userField
     * @return string|null
     */
    public static function onSearchIndex(array $userField): ?string
    {
        $res = '';
        if (!isset($userField['VALUE']))
        {
            return $res;
        }

        if (is_array($userField['VALUE']))
        {
            $val = $userField['VALUE'];
        }
        else
        {
            $val = [$userField['VALUE']];
        }

        Type\Collection::normalizeArrayValuesByInt($val);

        if (!empty($val) && Loader::includeModule('iblock'))
        {
            $iterator = Iblock\ElementTable::getList([
                'select' => [
                    'NAME',
                ],
                'filter' => [
                    '@ID' => $val,
                ],
            ]);
            while ($row = $iterator->fetch())
            {
                $res .= $row['NAME'] . "\r\n";
            }
            unset($row, $iterator);
        }
        unset($val);

        return $res;
    }

    /**
     * @param array $userField
     * @return bool|CDBResult
     */
    public static function getList(array $userField)
    {
        $iblockId = (int)($userField['SETTINGS']['IBLOCK_ID'] ?? 0);
        $activeFilter = (string)($userField['SETTINGS']['ACTIVE_FILTER'] ?? 'N');

        if (self::$iblockIncluded === null)
        {
            self::$iblockIncluded = Loader::includeModule('iblock');
        }
        if ($iblockId <= 0 || !self::$iblockIncluded)
        {
            return false;
        }

        $cacheTtl = 86400;

        $iblockRights = self::getIblockRightsMode($iblockId, $cacheTtl);
        if ($iblockRights === null)
        {
            return false;
        }

        $result = false;
        $filter = [
            'IBLOCK_ID' => $iblockId
        ];
        if ($iblockRights === Iblock\IblockTable::RIGHTS_SIMPLE)
        {
            if ($activeFilter === 'Y')
            {
                $filter['=ACTIVE'] = 'Y';
            }

            $rows = [];
            $elements = \Bitrix\Iblock\ElementTable::getList([
                'select' => [
                    'ID',
                    'NAME',
                ],
                'filter' => \CIBlockElement::getPublicElementsOrmFilter($filter),
                'order' => [
                    'NAME' => 'ASC',
                    'ID' => 'ASC',
                ],
                'cache' => [
                    'ttl' => $cacheTtl,
                ],
            ]);

            while($element = $elements->fetch())
            {
                $rows[] = $element;
            }
            unset($elements);

            if (!empty($rows))
            {
                $result = new \CIBlockElementEnum();
                $result->InitFromArray($rows);
            }
            unset($rows);
        }
        else
        {
            $filter['CHECK_PERMISSIONS'] = 'Y';
            $filter['MIN_PERMISSION'] = \CIBlockRights::PUBLIC_READ;
            if ($activeFilter === 'Y')
            {
                $filter['ACTIVE'] = 'Y';
            }

            $result = \CIBlockElement::GetList(
                [
                    'NAME' => 'ASC',
                    'ID' => 'ASC',
                ],
                $filter,
                false,
                false,
                [
                    'ID',
                    'NAME',
                ]
            );

            if($result)
            {
                $result = new \CIBlockElementEnum($result);
            }
        }

        return $result;
    }

    /**
     * @param array $userField
     * @param array $additionalParameters
     */
    public static function getEnumList(array &$userField, array $additionalParameters = []): void
    {
        if (self::$iblockIncluded === null)
        {
            self::$iblockIncluded = Loader::includeModule('iblock');
        }

        $userField['MANDATORY'] ??= 'N';
        $userField['SETTINGS']['IBLOCK_ID'] ??= 0;
        $userField['SETTINGS']['SHOW_NO_VALUE'] ??= 'Y';
        $userField['SETTINGS']['DISPLAY'] ??= '';
        $userField['SETTINGS']['ACTIVE_FILTER'] ??= 'N';

        if (
            !self::$iblockIncluded
            || (int)$userField['SETTINGS']['IBLOCK_ID']<= 0
        )
        {
            return;
        }

        $result = [];
        $showNoValue = (
            $userField['MANDATORY'] !== 'Y'
            || $userField['SETTINGS']['SHOW_NO_VALUE'] !== 'N'
            || (
                isset($additionalParameters['SHOW_NO_VALUE'])
                && $additionalParameters['SHOW_NO_VALUE'] === true
            )
        );

        if (
            $showNoValue
            && (
                $userField['SETTINGS']['DISPLAY'] !== 'CHECKBOX'
                || $userField['MULTIPLE'] !== 'Y'
            )
        )
        {
            $result = [
                null => static::getEmptyCaption($userField)
            ];
        }

        $filter = [];
        if (isset($additionalParameters['CURRENT_VALUES']))
        {
            if (is_array($additionalParameters['CURRENT_VALUES']))
            {
                Type\Collection::normalizeArrayValuesByInt($additionalParameters['CURRENT_VALUES']);
            }
            else
            {
                $additionalParameters['CURRENT_VALUES'] = (int)$additionalParameters['CURRENT_VALUES'];
                if ($additionalParameters['CURRENT_VALUES'] <= 0)
                {
                    $additionalParameters['CURRENT_VALUES'] = null;
                }
            }
            if (!empty($additionalParameters['CURRENT_VALUES']))
            {
                $filter['ID'] = $additionalParameters['CURRENT_VALUES'];
            }
        }
        $filter['ACTIVE'] = $userField['SETTINGS']['ACTIVE_FILTER'] === 'Y';

        $elements = self::getElements(
            (int)$userField['SETTINGS']['IBLOCK_ID'],
            $filter
        );

        if (!is_array($elements))
        {
            return;
        }

        if (!empty($additionalParameters['CURRENT_VALUES']))
        {
            $result = $elements;
        }
        else
        {
            $result = array_replace($result, $elements);
        }

        $userField['USER_TYPE']['FIELDS'] = $result;
    }

    public static function getDefaultValue(array $userField, array $additionalParameters = [])
    {
        $value = ($userField['SETTINGS']['DEFAULT_VALUE'] ?? '');
        return ($userField['MULTIPLE'] === 'Y' ? [$value] : $value);
    }

    protected static function getElements(int $iblockId, array $additionalFilter = [])
    {
        if (self::$iblockIncluded === null)
        {
            self::$iblockIncluded = Loader::includeModule('iblock');
        }
        if ($iblockId <= 0 || !self::$iblockIncluded)
        {
            return false;
        }

        $cacheTtl = 86400;

        $iblockRights = self::getIblockRightsMode($iblockId, $cacheTtl);
        if ($iblockRights === null)
        {
            return false;
        }

        if ($iblockRights === Iblock\IblockTable::RIGHTS_SIMPLE)
        {
            $filter = ['IBLOCK_ID' => $iblockId];
            if ($additionalFilter['ACTIVE'])
            {
                $filter['=ACTIVE'] = 'Y';
            }
            if (isset($additionalFilter['ID']))
            {
                $filter['@ID'] = $additionalFilter['ID'];
            }

            $result = [];
            $elements = \Bitrix\Iblock\ElementTable::getList([
                'select' => [
                    'ID',
                    'NAME',
                ],
                'filter' => \CIBlockElement::getPublicElementsOrmFilter($filter),
                'order' => [
                    'NAME' => 'ASC',
                    'ID' => 'ASC',
                ],
                'cache' => [
                    'ttl' => $cacheTtl,
                ],
            ]);

            while($element = $elements->fetch())
            {
                $result[$element['ID']] = $element['NAME'];
            }
            unset($element, $elements);

            if (empty($result))
            {
                $result = false;
            }
        }
        else
        {
            $filter = [
                'IBLOCK_ID' => $iblockId,
                'CHECK_PERMISSIONS' => 'Y',
                'MIN_PERMISSION' => \CIBlockRights::PUBLIC_READ,
            ];
            if ($additionalFilter['ACTIVE'])
            {
                $filter['ACTIVE'] = 'Y';
            }
            if (isset($additionalFilter['ID']))
            {
                $filter['ID'] = $additionalFilter['ID'];
            }

            $result = [];
            $iterator = \CIBlockElement::GetList(
                [
                    'NAME' => 'ASC',
                    'ID' => 'ASC',
                ],
                $filter,
                false,
                false,
                [
                    'ID',
                    'NAME',
                ]
            );

            while ($element = $iterator->Fetch())
            {
                $result[$element['ID']] = $element['NAME'];
            }
            unset($element, $iterator);
        }

        return $result;
    }

    private static function getIblockRightsMode(int $iblockId, int $cacheTtl): ?string
    {
        $iblock = Iblock\IblockTable::getRow([
            'select' => [
                'ID',
                'RIGHTS_MODE',
            ],
            'filter' => [
                '=ID' => $iblockId
            ],
            'cache' => [
                'ttl' => $cacheTtl,
            ],
        ]);

        return ($iblock['RIGHTS_MODE'] ?? null);
    }

    public static function renderEditForm(array $userField, ?array $additionalParameters): string
    {
        if($userField['MULTIPLE'] === 'N'){
            return parent::renderEditForm($userField, $additionalParameters);
        }

        $enum = call_user_func([$userField['USER_TYPE']['CLASS_NAME'], 'getlist'], $userField);

        if(!$enum)
        {
            return '';
        }

        $items = [];

        while($item = $enum->GetNext())
        {
            $items[$item['ID']] = $item;
        }

        $additionalParameters['items'] = $items;
        return self::renderEditFormCustom($userField, $additionalParameters);
    }

    public static function renderEditFormCustom(array $userField, ?array $additionalParameters): string
    {
        $additionalParameters['mode'] = self::MODE_EDIT_FORM;
        $userField['USE_COMPONENT'] = 'Y';
        return self::getHtmlCustom($userField, $additionalParameters);
    }

    private static function getHtmlCustom($userField, $additionalParameters): string
    {
        global $APPLICATION;

        $arProperty = [
            'ID' => $userField['ID'],
            'LINK_IBLOCK_ID' => (int)$userField['SETTINGS']['IBLOCK_ID'],
        ];
        $mxResultValue = static::GetValueForAutoCompleteMulti($arProperty,$additionalParameters);
        $strResultValue = (is_array($mxResultValue) ? htmlspecialcharsback(implode("\n",$mxResultValue)) : '');
        $strHTMLControlName["VALUE"] = $userField['FIELD_NAME'];

        ob_start();

        $fixIBlock = true;
        $control_id = $APPLICATION->IncludeComponent(
            "bitrix:main.lookup.input",
            "iblockedit",
            array(
                "CONTROL_ID" => preg_replace(
                    "/[^a-zA-Z0-9_]/i",
                    "x",
                    $strHTMLControlName["VALUE"].'_'.mt_rand(0, 10000)
                ),
                "INPUT_NAME" => $strHTMLControlName['VALUE'].'[]',
                "INPUT_NAME_STRING" => "inp_".$strHTMLControlName['VALUE'],
                "INPUT_VALUE_STRING" =>  $strResultValue,
                "MULTIPLE" => $userField['MULTIPLE'],
                "IBLOCK_ID" => (int)$userField['SETTINGS']['IBLOCK_ID'],
                'WITHOUT_IBLOCK' => 'Y',
                'FILTER' => 'Y'
            ), null, array("HIDE_ICONS" => "Y")
        );

        $APPLICATION->IncludeComponent(
            'bitrix:main.tree.selector',
            'iblockedit',
            array(
                "INPUT_NAME" => $strHTMLControlName['VALUE'],
                'ONSELECT' => 'jsMLI_'.$control_id.'.SetValue',
                'MULTIPLE' => $userField['MULTIPLE'],
                'SHOW_INPUT' => 'N',
                'SHOW_BUTTON' => 'Y',
                'GET_FULL_INFO' => 'Y',
                "START_TEXT" => Loc::getMessage("BT_UT_EAUTOCOMPLETE_MESS_LIST_INVITE"),
                'BUTTON_CAPTION' => Loc::getMessage('BT_UT_EAUTOCOMPLETE_MESS_CHOOSE_ELEMENT'),
                'BUTTON_TITLE' => Loc::getMessage('BT_UT_EAUTOCOMPLETE_MESS_CHOOSE_ELEMENT_MULTI_DESCR'),
                "NO_SEARCH_RESULT_TEXT" => Loc::getMessage("BT_UT_EAUTOCOMPLETE_MESS_NO_SEARCH_RESULT_TEXT"),
                "IBLOCK_ID" => (int)$userField['SETTINGS']['IBLOCK_ID'],
                'WITHOUT_IBLOCK' => 'Y',
            ), null, array("HIDE_ICONS" => "Y")
        );

        return ob_get_clean();
    }

    public static function GetValueForAutoCompleteMulti($arProperty, $arValues, $arBanSym = '', $arRepSym = '')
    {
        $arResult = [];

        if (is_array($arValues))
        {
            if (array_key_exists('VALUE', $arValues))
            {
                if (is_array($arValues['VALUE']))
                {
                    $arValues = $arValues['VALUE'];
                }
                else
                {
                    $arValues = [$arValues['VALUE']];
                }
            }
            foreach ($arValues as $intPropertyValueID => $arOneValue)
            {
                if (!is_array($arOneValue))
                {
                    $strTmp = $arOneValue;
                    $arOneValue = array(
                        'VALUE' => $strTmp,
                    );
                }
                $mxResult = static::GetPropertyValue($arProperty,$arOneValue);
                if (is_array($mxResult))
                {
                    $arResult[$intPropertyValueID] = htmlspecialcharsbx(str_replace($arBanSym,$arRepSym,$mxResult['~NAME'])).' ['.$mxResult['ID'].']';
                }
            }
        }

        return !empty($arResult) ? $arResult : false;
    }

    protected static function GetPropertyValue($arProperty, $arValue)
    {
        $mxResult = false;
        if ((int)($arValue['VALUE'] ?? 0) > 0)
        {
            $mxResult = static::GetLinkElement((int)$arValue['VALUE'], (int)$arProperty['LINK_IBLOCK_ID']);
            if (is_array($mxResult))
            {
                $mxResult['PROPERTY_ID'] = $arProperty['ID'];
                if (isset($arProperty['PROPERTY_VALUE_ID']))
                {
                    $mxResult['PROPERTY_VALUE_ID'] = $arProperty['PROPERTY_VALUE_ID'];
                }
                else
                {
                    $mxResult['PROPERTY_VALUE_ID'] = false;
                }
            }
        }

        return $mxResult;
    }

    protected static function GetLinkElement($elementId, $iblockId)
    {
        static $cache = [];

        $iblockId = (int)$iblockId;
        if ($iblockId <= 0)
            $iblockId = 0;
        $elementId = (int)$elementId;
        if ($elementId <= 0)
            return false;
        if (!isset($cache[$elementId]))
        {
            $arFilter = [];
            if ($iblockId > 0)
                $arFilter['IBLOCK_ID'] = $iblockId;
            $arFilter['ID'] = $elementId;
            $arFilter['SHOW_HISTORY'] = 'Y';
            $rsElements = \CIBlockElement::GetList([], $arFilter, false, false, ['IBLOCK_ID','ID','NAME']);
            if ($arElement = $rsElements->GetNext())
            {
                $arResult = array(
                    'ID' => $arElement['ID'],
                    'NAME' => $arElement['NAME'],
                    '~NAME' => $arElement['~NAME'],
                    'IBLOCK_ID' => $arElement['IBLOCK_ID'],
                );
                $cache[$elementId] = $arResult;
            }
            else
            {
                $cache[$elementId] = false;
            }
        }

        return $cache[$elementId];
    }
}