В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ограничением: реквизиты регистра сведений не могут напрямую содержать такие типы данных, как СписокЗначений, Массив или Структура. Это связано с особенностями хранения данных в реляционных таблицах СУБД. Однако задачи, требующие привязки набора элементов (например, списка номенклатуры) к одной записи регистра, встречаются повсеместно — от настроек регламентных заданий до сложных фильтров в аналитических отчетах.
В этой статье мы подробно разберем, как можно элегантно обойти это ограничение, рассмотрим различные способы реализации — от простых «костылей» до архитектурно правильных решений, и проанализируем, как обеспечить удобство работы пользователя.
Это классический метод, позволяющий упаковать любой объект 1С (включая СписокЗначений) в бинарный формат и сохранить его в базе данных. Рассмотрим подробнее, как реализовать этот механизм в форме записи регистра.
При использовании этого метода сам реквизит регистра должен иметь тип ХранилищеЗначения. Поскольку напрямую редактировать этот тип на форме нельзя, нам потребуется вспомогательный реквизит формы.
Алгоритм реализации:
ДанныеСписка с типом ХранилищеЗначения — для этой задачи подойдет универсальный редактор записей регистров с поддержкой Хранилища значения.СписокДляРедактирования с типом СписокЗначений.ПриЧтенииНаСервере) будем извлекать данные из хранилища, используя распаковщик для работы с данными.ПередЗаписьюНаСервере) — упаковывать обратно.Проанализируем пример кода для формы записи:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Извлекаем список из хранилища значения
ЗначениеИзХранилища = ТекущийОбъект.ДанныеСписка.Получить();
Если ТипЗнч(ЗначениеИзХранилища) = Тип("СписокЗначений") Тогда
ЗначениеВРеквизитФормы(ЗначениеИзХранилища, "СписокДляРедактирования");
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Помещаем список обратно в хранилище перед сохранением
ТекущийОбъект.ДанныеСписка = Новый ХранилищеЗначения(РеквизитФормыВЗначение("СписокДляРедактирования"));
КонецПроцедуры
Преимущества: Позволяет хранить объекты практически любого объема и сложности.
Недостатки: Данные в ХранилищеЗначения недоступны для использования в условиях запроса (нельзя написать ГДЕ Номенклатура В Рег.ДанныеСписка). Также невозможно вывести содержимое списка в стандартный динамический список без дополнительных ухищрений.
Если нам нужно хранить список ссылок (например, массив Номенклатура), мы можем преобразовать его в текстовую строку. В современных версиях платформы 1С 8.3 для этого лучше всего подходит формат JSON, так как он более компактен по сравнению с XML.
Разберем ситуацию, когда мы преобразуем список в строку неограниченной длины, используя выгрузку данных в формате JSON. Для этого добавим реквизит СериализованныйСписок с типом Строка.
Рассмотрим пример программной упаковки данных через XDTO (как предлагалось в обсуждении) или экспорт данных в JSON:
// Пример сериализации списка в XML строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, СписокДляСохранения);
СтрокаДляБазы = ЗаписьXML.Закрыть();
Однако мы рекомендуем использовать более современный JSON. Посмотрим на пример:
// Упаковка в JSON
МассивСсылок = СписокЗначений.ВыгрузитьЗначения();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивСсылок);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Распаковка из JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
МассивСсылок = ПрочитатьJSON(ЧтениеJSON);
СписокЗначений.ЗагрузитьЗначения(МассивСсылок);
Этот метод удобен тем, что строку можно просмотреть в консоли запросов (предварительно возможна проверка на заполненность обязательных полей), но она всё еще неэффективна для поиска конкретных элементов внутри запроса.
Если список номенклатуры нужен для подстановки в запросы или построения отчетов, то правильным с точки зрения архитектуры реляционных баз данных будет отказ от хранения всего списка в одной ячейке. Вместо этого мы создаем несколько записей в регистре сведений, где одно измерение — это «Владелец списка» (например, ключ настройки), а второе измерение — сама Номенклатура.
Разберем ситуацию: пользователю неудобно вводить 20 строк вручную. Выясним, как сделать интерфейс удобным, сохранив правильную структуру данных.
Решение: Создание формы набора записей с виртуальной таблицей.
Номенклатура является измерением.Проанализируем код записи такого набора:
НаборЗаписей = РегистрыСведений.НастройкиОтборов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.КлючНастройки.Установить(ТекущийКлюч);
Для Каждого Стр Из ТаблицаНаФорме Цикл
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.КлючНастройки = ТекущийКлюч;
НоваяЗапись.Номенклатура = Стр.Номенклатура;
КонецЦикла;
НаборЗаписей.Записать();
Это решение — самое производительное. Оно позволяет использовать данные регистра в любых соединениях и условиях запросов без предварительной обработки в коде.
Независимо от выбранного способа хранения (в строке или хранилище), пользователи часто хотят видеть состав списка прямо в основной таблице регистра, не заходя в каждую запись. Выясним, как это реализовать.
Для отображения содержимого рекомендуется завести дополнительный реквизит ПредставлениеСписка (Тип: Строка, длина например 150-200 символов). При записи объекта мы будем программно формировать строку из первых нескольких элементов списка (например: "Ботинки, Туфли, Кроссовки...").
Рассмотрим, как это помогает производительности:
ХранилищеЗначения для каждой строки при прокрутке списка.Важный момент, который мы должны проанализировать — это удаление объектов. Если вы храните ссылку на Номенклатуру внутри сериализованной строки или ХранилищаЗначения, платформа не сможет отследить эту связь. Если пользователь удалит карточку номенклатуры, в вашем списке останется «битая ссылка» (объект не найден) — проблему решает автоматическая диагностика и удаление битых ссылок в регистрах.
Как минимизировать риски:
ПередУдалением для справочника номенклатуры, которая будет проверять наличие ссылок в ваших специфических хранилищах. Однако это технически сложно и замедляет работу системы.Выбор метода зависит от ваших целей:
ХранилищеЗначения. Это проще всего в реализации.Помните, что удобство интерфейса (UX) можно обеспечить даже при самой строгой и «неудобной» структуре данных, если правильно использовать возможности форм и наборов записей.