Кастомный тип свойства для JSON-данных с валидацией
Проблема
При работе со сложными структурированными данными в инфоблоках часто приходится хранить JSON в обычных строковых свойствах. Это создает проблемы: нет валидации при сохранении, неудобный интерфейс редактирования и риск повреждения данных. Стандартные типы свойств Битрикс не предоставляют встроенных механизмов для работы с JSON-структурами.
Решение
Создание кастомного типа свойства позволяет реализовать специализированный интерфейс с валидацией и удобным редактором. Регистрируем новый тип свойства через событие.
Регистрация типа свойства:
<?php
// /local/php_interface/init.php
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
'iblock',
'OnIBlockPropertyBuildList',
['CustomPropertyJson', 'GetUserTypeDescription']
);
class CustomPropertyJson
{
public static function GetUserTypeDescription(): array
{
return [
'PROPERTY_TYPE' => 'S',
'USER_TYPE' => 'json_data',
'DESCRIPTION' => 'JSON данные',
'GetPropertyFieldHtml' => [__CLASS__, 'GetPropertyFieldHtml'],
'ConvertToDB' => [__CLASS__, 'ConvertToDB'],
'ConvertFromDB' => [__CLASS__, 'ConvertFromDB'],
];
}
// Валидация и сохранение
public static function ConvertToDB($property, $value)
{
if (empty($value['VALUE'])) {
return $value;
}
// Проверяем валидность JSON
$decoded = json_decode($value['VALUE'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
global $APPLICATION;
$APPLICATION->ThrowException('Невалидный JSON: ' . json_last_error_msg());
return false;
}
// Дополнительная валидация структуры
if (isset($property['USER_TYPE_SETTINGS']['REQUIRED_FIELDS'])) {
$required = explode(',', $property['USER_TYPE_SETTINGS']['REQUIRED_FIELDS']);
foreach ($required as $field) {
if (!isset($decoded[trim($field)])) {
global $APPLICATION;
$APPLICATION->ThrowException("Отсутствует обязательное поле: {$field}");
return false;
}
}
}
return $value;
}
// Форматирование при выводе
public static function ConvertFromDB($property, $value)
{
$value['VALUE'] = htmlspecialcharsback($value['VALUE']);
return $value;
}
// Интерфейс редактирования
public static function GetPropertyFieldHtml($property, $value, $strHTMLControlName)
{
$currentValue = $value['VALUE'];
return '<textarea
name="' . $strHTMLControlName['VALUE'] . '"
rows="10"
cols="50"
class="json-editor"
placeholder=\'{"key": "value"}\'
style="font-family: monospace; font-size: 14px; line-height: 1.4; padding: 8px; border: 1px solid #ccc; border-radius: 4px; width: 100%;">' .
htmlspecialchars($currentValue) .
'</textarea>
<script>
BX.ready(function() {
const textarea = document.querySelector(\'textarea[name="' . $strHTMLControlName['VALUE'] . '"]\');
// Автоформатирование при потере фокуса
textarea.addEventListener("blur", function() {
try {
if (this.value.trim()) {
const obj = JSON.parse(this.value);
this.value = JSON.stringify(obj, null, 2);
this.style.borderColor = "#28a745";
}
} catch(e) {
this.style.borderColor = "#dc3545";
console.error("JSON error:", e);
}
});
// Проверка при вводе
textarea.addEventListener("input", function() {
try {
if (this.value.trim()) {
JSON.parse(this.value);
this.style.borderColor = "#28a745";
} else {
this.style.borderColor = "#ccc";
}
} catch(e) {
this.style.borderColor = "#dc3545";
}
});
// Инициализация проверки
textarea.dispatchEvent(new Event("input"));
});
</script>';
}
}
Итог
Кастомный тип свойства обеспечивает типобезопасность, валидацию данных на уровне сохранения и улучшает UX редактирования. Подход масштабируется для любых специфических форматов данных.