Универсальные функции получения значений реквизитов объектов (8.2+)

14.05.13

Разработка - Универсальные функции

Довольно часто в алгоритмах приходится обращаться к значениям реквизитов различных объектов "через точку". При этом, если объект ссылочного типа, то возможно многократное обращение к БД. В данной статье попробуем унифицировать и оптимизировать этот процесс.

Общий принцип оптимизации заключается в сокрашении количества обращений к базе данных. Для этого в линейном алгоритме анализируем, что за объект перед нами, если это ссылка, то описываем запрос, с помощью которого получаем все данные и в дальнейшем работаем с результатом запроса.

Задача: Описать функции позволяющие получить значения реквизитов различных объектов (независимо от типа объекта). Но ограничимся объектами метаданных конфигурации, которые могут иметь ссылочный тип данных: Справочники, Документы и т.п.

Описанные далее функции желательно располагать в общем серверном модуле без возможности вызова сервера. Если будет возможность вызывать данные функции с клиента, то злоумышленник сможет получить любые данные из базы без особого труда.

Определимся с параметрами:

  1. Объект - объект, данные которого необходимо получить;
  2. ИменаРеквизитов - Список имен реквизитов, данные которых необходимо получить. Если спиок не указан, то предполагаем, что нужно получить данные всех реквизитов;
  3. ДополнительныеРеквизиты - Структура, с помошью которой можно было бы описать дополнительные данные, которые нужно получить вместе с реквизитами объекта (без использования явных соединений с другими таблицами) или выполнение каких-то действий с полями выборки на языке запросов. В ключе элемента структуры описываем имя реквизита в общем списке реквизитов объекта, в значении - поле выборки или алгоритм обработки полей выборки на языке запросов.

Функция ДанныеРеквизитовОбъекта(Объект, ИменаРеквизитов = Неопределено,
                               
ДополнительныеРеквизиты = Неопределено) Экспорт

Разберем, значения каких реквизитов необходимо получить из базы и приведем список имен к типу данных Массив.

    СтруктураОбъекта = Новый Структура;
   
МетаданныеОбъекта = Объект.Метаданные();

    Если
ИменаРеквизитов = Неопределено тогда
       
МассивИменРеквизитов = МассивИменРеквизитовОбъекта(Объект);
    иначе
        Если
ТипЗнч(ИменаРеквизитов) = Тип("Массив") тогда
           
МассивИменРеквизитов = ИменаРеквизитов;
        иначеЕсли
ТипЗнч(ИменаРеквизитов) = Тип("Строка") тогда
           
МассивИменРеквизитов = МассивПодстрокИзСтроки(ИменаРеквизитов);
        КонецЕсли;
    КонецЕсли;

Если список имен не задан, то с помощью функции МассивИменРеквизитовОбъекта() получим массив имен всех реквизитов объекта (алгоритм этой функции смотри ниже).

Если список имен задан в виде строки разделенной запятыми, то разложим ее на подстроки с помошью функции МассивПодстрокИзСтроки(). Это не сложная задача, алгоритм этой функции разбирать не будем.

Далее с помошью функции ЭтоСсылка() определим, является ли объект ссылочным типом. Для ссылочных типов значения необходимо получить из БД, в противном случае значения реквизитов хранятся в памяти.

    ЭтоСсылка = ЭтоСсылка(Объект);
   
СсылкаОбъекта = ?(ЭтоСсылка, Объект, Объект.Ссылка);

   
ПолучитьДанныеИзОбъекта = НЕ ЭтоСсылка;
    Если
ЭтоСсылка И СсылкаОбъекта.Пустая() тогда
       
// это пустая ссылка (данных в базе нет)
       
ПолучитьДанныеИзОбъекта = Истина;
    КонецЕсли;

Теперь, если данные объекта хранятся в памяти - мы можем их сразу получить, в противном случае необходио подготовиться для составления запроса к БД (переведем список имен реквизитов из типа данных Массив в тип Структура).

    ОсновныеРеквизиты = Новый Структура;
    Для каждого
ИмяРеквизита Из МассивИменРеквизитов Цикл
        Если
ПолучитьДанныеИзОбъекта тогда
           
СтруктураОбъекта.Вставить(ИмяРеквизита, Объект[ИмяРеквизита]);
        иначе
           
ОсновныеРеквизиты.Вставить(ИмяРеквизита);
        КонецЕсли;
    КонецЦикла;

Все готово к составлению запроса чтобы получить значения реквизитов если перед нами объект ссылочного типа, а так же для получения дополнительных реквизитов, описанных в 3-ем параметре функции. Но смысл всех этих действий есть, только если данный объект существует в БД (на него есть ссылка).

Для начала составим текст запроса для полей выборки поочередно обходя структуры Основных и Дополнительных реквизитов

    Если НЕ СсылкаОбъекта.Пустая() тогда

       
// сформируем текст выборки по реквизитам
       
ТекстВыборкиРеквизиты = "";
        Для
индекс = 0 по 1 Цикл
           
СтруктураРеквизитов = ?(индекс = 0, ОсновныеРеквизиты, ДополнительныеРеквизиты);
            Если
СтруктураРеквизитов = Неопределено тогда
                Продолжить;
            КонецЕсли;
            Для каждого
ЭлементСтруктуры из СтруктураРеквизитов Цикл
                Если НЕ
МетаданныеОбъекта.ТабличныеЧасти.Найти(ЭлементСтруктуры.Ключ) = Неопределено тогда
                   
// это имя табличной части
                   
Продолжить;
                КонецЕсли;
               
ТекстВыборкиРеквизиты = ТекстВыборкиРеквизиты
                     + ?(ПустаяСтрока(ТекстВыборкиРеквизиты), "", "," + Символы.ПС)
                     + ?(
ЗначениеЗаполнено(ЭлементСтруктуры.Значение), ЭлементСтруктуры.Значение, ЭлементСтруктуры.Ключ)
                     +
" КАК " + ЭлементСтруктуры.Ключ;
            КонецЦикла;
        КонецЦикла;

Теперь соберем текст запроса и выполним его. Результат запроса добавим в общую структуру данных объекта

        // получим реквизиты одним запросом
       
Если НЕ ПустаяСтрока(ТекстВыборкиРеквизиты) тогда
           
Запрос = Новый Запрос;
           
Запрос.Текст = "ВЫБРАТЬ
                           |"
+ ТекстВыборкиРеквизиты + "
                           |ИЗ
                           |    "
+ МетаданныеОбъекта.ПолноеИмя() + "
                           |ГДЕ
                           |    Ссылка = &Ссылка"
;
           
Запрос.УстановитьПараметр("Ссылка", СсылкаОбъекта);
           
РезультатЗапроса = Запрос.Выполнить();
           
Выборка = РезультатЗапроса.Выбрать();
            Если
Выборка.Следующий() тогда
                Для каждого
Колонка из РезультатЗапроса.Колонки Цикл
                   
СтруктураОбъекта.Вставить(Колонка.Имя, Выборка[Колонка.Имя]);
                КонецЦикла;
            КонецЕсли;
        КонецЕсли;

    КонецЕсли;

Все данные получены, можно завершать функцию

    Возврат СтруктураОбъекта;

КонецФункции
// ДанныеРеквизитовОбъекта()

Достоинства алгоритма:

  1. Может работать с различными типами объектов конфигурации (которые могут иметь ссылки: Справочники, Документы, ПВХ и т.п.);
  2. Получение всех реквизитов за одно обращение к БД;
  3. Данные возвращаются в виде структуры, что позволяет их передать далее на клиент без преобразования;
  4. С помошью Дополнительных реквизитов можно получить вспомогательные данные (обращение к полям выборки через несколько точек) или на уровне запроса выполнить какие-то действия с данными.
  5. Наглядность кода, данные получаются одной функцией, без надобности каждый раз писать запрос к БД с обработкой результата.

Недостатки:

  1. Не реализована возможность получения табличных частей объектов

Дополнительные функции использованные в алгоритме:

// Возвращает массив имен всех реквизитов переданного объекта
//
Функция МассивИменРеквизитовОбъекта(Объект) Экспорт

   
МассивИменРеквизитов = Новый Массив;

    Если
ТипЗнч(Объект) = Тип("ОбъектМетаданных") тогда
       
МетаданныеОбъекта = Объект;
    иначе
       
МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Объект));
        Если
МетаданныеОбъекта = Неопределено тогда
            Возврат
МассивИменРеквизитов;
        КонецЕсли;
    КонецЕсли;

    Для
индекс = 0 по 1 Цикл
       
КоллекцияРеквизитов = ?(индекс = 0, МетаданныеОбъекта.СтандартныеРеквизиты, МетаданныеОбъекта.Реквизиты);
        Для Каждого
Реквизит Из КоллекцияРеквизитов Цикл
           
МассивИменРеквизитов.Добавить(Реквизит.Имя);
        КонецЦикла;
    КонецЦикла;
    Для каждого
ОбщийРеквизит Из Метаданные.ОбщиеРеквизиты Цикл
        Если
ИспользуетсяОбщийРеквизит(ОбщийРеквизит, МетаданныеОбъекта) тогда
           
МассивИменРеквизитов.Добавить(ОбщийРеквизит.Имя);
        КонецЕсли;
    КонецЦикла;

    Возврат
МассивИменРеквизитов;

КонецФункции

// Проверяет используется ли в Объекте указанный общий реквизит
//
Функция ИспользуетсяОбщийРеквизит(ОбщийРеквизит, Объект) Экспорт

    Если
ТипЗнч(Объект) = Тип("ОбъектМетаданных") тогда
       
МетаданныеОбъекта = Объект;
    иначе
       
МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(Объект));
        Если
МетаданныеОбъекта = Неопределено тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЕсли;

    Если
ТипЗнч(ОбщийРеквизит) = Тип("ОбъектМетаданных") тогда
       
МетаданныеОбщегоРеквизита = ОбщийРеквизит;
    иначе
       
МетаданныеОбщегоРеквизита = Метаданные.ОбщиеРеквизиты.Найти(ОбщийРеквизит);
        Если
МетаданныеОбщегоРеквизита = Неопределено тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЕсли;

   
ЭлементСостава = МетаданныеОбщегоРеквизита.Состав.Найти(МетаданныеОбъекта);
    Если
ЭлементСостава = Неопределено тогда
        Возврат Ложь;
    КонецЕсли;

   
пИспользованиеОбщегоРеквизита = Метаданные.СвойстваОбъектов.ИспользованиеОбщегоРеквизита;
    Если
ЭлементСостава.Использование = пИспользованиеОбщегоРеквизита.Использовать тогда
        Возврат Истина;
    иначеЕсли
ЭлементСостава.Использование = пИспользованиеОбщегоРеквизита.НеИспользовать тогда
        Возврат Ложь;
    иначе
       
пАвтоИспользованиеОбщегоРеквизита = Метаданные.СвойстваОбъектов.АвтоИспользованиеОбщегоРеквизита;
        Если
МетаданныеОбщегоРеквизита.АвтоИспользование = пАвтоИспользованиеОбщегоРеквизита.Использовать тогда
            Возврат Истина;
        иначе
            Возврат Ложь;
        КонецЕсли;
    КонецЕсли;

КонецФункции

См. также

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    12135    dimanich70    80    

125

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    3409    3    John_d    11    

57

Универсальные функции Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    8606    atdonya    22    

55

Универсальные функции Программист Платформа 1С v8.3 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    4783    ke.92@mail.ru    16    

65

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    11632    YA_418728146    7    

156

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2866    43    progmaster    8    

4

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    17275    152    sapervodichka    112    

134
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. ildarovich 7898 15.05.13 10:48 Сейчас в теме
попробуем унифицировать и оптимизировать этот процесс
- Под процессом подразумевается
обращение к значениям реквизитов различных объектов "через точку"?
- Критерием оптимизации является время доступа к реквизитам? - Тогда использование этих функций скорее всего будет ошибкой - время доступа в среднем довольно значительно возрастет (попробуйте провести эксперименты). Дело в том, что платформа сама довольно эффективно кэширует объекты и в большинстве случаев не производит многократного обращения к БД. Поэтому польза от такого доморощенного кэширования представляется весьма сомнительной, зато накладные расходы очевидны.
native-api; +1 Ответить
3. zling 18 15.05.13 10:50 Сейчас в теме
(1) опередил, пока я аватар искал))
2. zling 18 15.05.13 10:49 Сейчас в теме
Serge F, Так ведь все равно кеширование существует. Просто нужно первым считывать не наименование или код, а любой другой реквизит, тогда объект кешируется полностью. Также и в данном случае, объект или несколько объектов будут в кеше + в данной структуре. Из плюсов остается преобразование данных в структуру для передачи. Для ТЧ можно массив таких структур использовать
4. zling 18 15.05.13 10:55 Сейчас в теме
опять же лишние данные на клиент с сервера тягать - лишний трафик, так вот
5. kyrasol 51 15.05.13 11:39 Сейчас в теме
Возможно, что при полном чтении всех реквизитов кеширование объекта самой платформы будет выигрывать... Но если мне нужно только пару-тройку реквизитов + еще один подчиненный через точку и больше от объекта мне ничего не надо? в таком случае я получу только эти значения без кеширования всех реквизитов 1С-кой
6. vandalsvq 1574 15.05.13 13:01 Сейчас в теме
В БСП есть аналогичные методы.

Отличия от предложенного решения:
- 1С не проверяют существование реквизита, таким образом это ложится на плечи программиста. НО: считаю что это правильно, ибо лишние затраты на проверку метаданных.
- у них можно передавать не только строку, но и структуру, таким образом можно исхитрившись получить табличную часть.
native-api; CratosX; StepByStep; +3 Ответить
7. kyrasol 51 15.05.13 13:11 Сейчас в теме
(6) где в БСП их можно найти? Функции писались самостоятельно с нуля, охота посмотреть на реализацию от 1С...
native-api; +1 Ответить
10. vandalsvq 1574 15.05.13 14:35 Сейчас в теме
(7) см. ОбщийМодуль в БСП 2.1, методы ЗначениеРеквизитаОбъекта, ЗначениеРеквизитовОбъекта и т.д. В более ранних версиях они именовались ПолучитьЗначениеРеквизита, ПолучитьЗначенияРеквизитов.
native-api; cfifgoth; +2 Ответить
8. yuraos 1001 15.05.13 13:36 Сейчас в теме

Далее с помошью функции ЭтоСсылка() определим, является ли объект ссылочным типом. Для ссылочных типов значения необходимо получить из БД, в противном случае значения реквизитов хранятся в памяти.

ЭтоСсылка = ЭтоСсылка(Объект);


что это за загадочная функция ???
11. kyrasol 51 15.05.13 17:22 Сейчас в теме
(8)ЭтоСсылка() возвращает истина если переданный параметр является ссылкой
например, проверив принадлежность типа объекта к ссылочным типам
Справочники.ТипВсеСсылки().СодержитТип(ТипОбъекта)
ИЛИ Документы.ТипВсеСсылки().СодержитТип(ТипОбъекта)
ИЛИ Перечисления.ТипВсеСсылки().СодержитТип(ТипОбъекта)
и т.д.
jobkostya1c_ERP; +1 Ответить
12. yuraos 1001 15.05.13 18:15 Сейчас в теме
(11) ммммм...
семантика функции по ее названию ясна и реализация ее тоже примерно понятна.
НО:
Функция явно не типовая (В УПП-1.2 такой нет, есть похожая с другим названием).
Так-что неполохо бы привести ее реализацию в статье.
13. yuraos 1001 15.05.13 18:17 Сейчас в теме
(12)
ЗЫ:
А то я по простоте душевной было подумал,
что под какими-то 8.2.х платформами такую встроенную функцию забабахали.
9. yuraos 1001 15.05.13 13:45 Сейчас в теме

Описанные далее функции желательно располагать в общем серверном модуле без возможности вызова сервера. Если будет возможность вызывать данные функции с клиента, то злоумышленник сможет получить любые данные из базы без особого труда.


Ох уж эти злоумышленники с углубленным знанием 1С,
видать они недавно устроили массовую атаку со взломом ...
...
раз уж так часто в последнее время на Инфостарте
заботятся о информационной безопасности прикладных решений
в самых извращенных ситуациях
14. Трактор 1249 16.05.13 13:24 Сейчас в теме
15. PlatonStepan 38 16.05.13 16:52 Сейчас в теме
Плохо что нет замеров.
В чём выгода неясно.
Я правильно понял принцип?:
КэшРеквизитовОбъекта = Новый ФиксированнаяСтруктура(ПодготовленнаяСтруктураМетаданных[ТипЗнч(Объект));
ЗаполнитьЗначенияСвойств(КэшРеквизитовОбъекта, Объект);
16. ineshyk 16.05.13 19:50 Сейчас в теме
как Вы контролируете "промах" кэша, когда объект в памяти будет отличатся от объекта БП.
чем не устроила функция в БСП ОбщегоНазначения.ПолучитьЗначенияРеквизитов(Ссылка, ИменаРеквизитов)?
17. Aleksey.Bochkov 3671 16.05.13 22:06 Сейчас в теме
И ни одного минуса за такой "велосипед".. непорядок :).
Трактор; +1 Ответить
18. Abadonna 3964 18.05.13 06:00 Сейчас в теме
(17)
И ни одного минуса за такой "велосипед"

А чего минусить, пусть балуется ;) Во всяком случае, не "Отчет по ДР сотрудников" :)))
Трактор; borman; +2 Ответить
19. V.Nikonov 120 27.05.13 17:38 Сейчас в теме
Ну, не всякий сможет начинать с "ПодсистемаРазработчика"...
А очки зарабатывать хлопцу надо, дабы безболезненно пользоваться чужими наработками.
20. Gilev.Vyacheslav 1916 27.05.13 17:52 Сейчас в теме
21. V.Nikonov 120 27.05.13 20:28 Сейчас в теме
(20) Gilev.Vyacheslav, насчет качества первых статей у меня лично претензий нет. От себя Плюс поставил.
Просто, есть общая проблема "новичков", при недостаточном количестве собственных публикаций (а у Новичка - это по определению), считаю "нормальным" ставить плюсы, даже не если не "Супер шедевр" публикуется. Хуже - отмечаться на Инфостарте поливая никому не интересную "воду".
22. jobkostya1c_ERP 100 12.02.18 16:53 Сейчас в теме
Описанные далее функции желательно располагать в общем серверном модуле без возможности вызова сервера. Если будет возможность вызывать данные функции с клиента, то злоумышленник сможет получить любые данные из базы без особого труда.
Понравилось особенно. Долго искал вызов функции в стандартной конфе на основе БСП в модулях с галкой "Вызов сервера". Теперь понятно почему :)
Оставьте свое сообщение