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