Мои шаблоны. Нахождение всех родителей в запросе




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

19 Comments

  1. jobkostya1c8

    Что-то сильно просто. Такие задачи дают на собеседованиях в крутые фирмы. Из разряда «По регистру Счета учета номенклатуры (УТ, БП) для списка входной номенклатуры если не указаны счета смотреть счета по группе номенклатуры и так до самого верха».

    Самое веселое, что если даже такие задачи на фрилансе или в ИТ-отделе редко попадаются все-равно для быстроты решаются кодом. Запросом — надо как-то склеивать или специально искать — только если оправдано по большой загрузке. И так по всем случаям.

    Также часть в практике задача получения всех подчиненных документов или документов-оснований. Вот эти примеры более полезны чтоб иметь библиотеку под рукой.

    Reply
  2. jobkostya1c8

    К тому же конструкция запроса В ИЕРАРХИИ(&параметр) сама очень не оптимально. Используется только для простоты.

    Reply
  3. Lapitskiy

    (1) kostyaomsk, точно, на собеседовании почему то всех интересует «виртуальный конь в вакууме»

    Reply
  4. wolfsoft
    Решение с сайта 1с обладает существенным ограничением: нужно заранее знать количество уровней.

    Как бы нет. Просто выборка идёт по 5 уровней, но само количество уровней не ограничено. Другое дело, что запрос в цикле.

    Reply
  5. Steelvan

    // По входящей ссылке формирует дерево подчиненных объектов из элементов Дерева процесов.

    /

    /
    Процедура СформироватьДеревоОбъектаСПодчиненными(ДеревоОбъектаСПодчиненными = Неопределено, СсылкаВладельца, тзИсточник = Неопределено) Экспорт
    
    // Создание возвращаемого дерева
    Если ДеревоОбъектаСПодчиненными = Неопределено тогда
    ДеревоОбъектаСПодчиненными  = Новый ДеревоЗначений;
    ДеревоОбъектаСПодчиненными.Колонки.Добавить(«СсылкаЭлемента»);
    ДеревоОбъектаСПодчиненными.Колонки.Добавить(«Наименование»);
    ДеревоОбъектаСПодчиненными.Колонки.Добавить(«СтрокаНавигационнойСсылкиНаКартинку»);
    КонецЕсли;
    
    // Получение данных для исходной строки
    Если тзИсточник = Неопределено Тогда
    
    // Получаю тз, отсортированную по номерам из регистра сведений
    лпЗапросЭлементов           = Новый Запрос;
    лпЗапросЭлементов.Текст = «ВЫБРАТЬ
    | осЭлементыДереваПроцессов.СсылкаВладельца,
    | осЭлементыДереваПроцессов.Ссылка КАК СсылкаЭлемента,
    | осЭлементыДереваПроцессов.Ссылка.Наименование КАК Наименование,
    | осЭлементыДереваПроцессов.КартинкаЭлемента.СтрокаНавигационнойСсылкиНаКартинку КАК СтрокаНавигационнойСсылкиНаКартинку
    |ИЗ
    | Справочник.осЭлементыДереваПроцессов КАК осЭлементыДереваПроцессов
    |  ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.осНумерацияЭлементовДП КАК осНумерацияЭлементовДП
    |  ПО (осНумерацияЭлементовДП.СсылкаЭлемента = осЭлементыДереваПроцессов.Ссылка)
    |   И (осНумерацияЭлементовДП.СсылкаВладельца = осЭлементыДереваПроцессов.СсылкаВладельца)
    |ГДЕ
    | осЭлементыДереваПроцессов.ПометкаУдаления = ЛОЖЬ
    |
    |ДЛЯ ИЗМЕНЕНИЯ
    | Справочник.осЭлементыДереваПроцессов,
    | РегистрСведений.осНумерацияЭлементовДП
    |
    |УПОРЯДОЧИТЬ ПО
    | осНумерацияЭлементовДП.НомерПорядка»;
    тзИсточник = лпЗапросЭлементов.Выполнить().Выгрузить();
    КонецЕсли;
    
    // Добавление строки в возвращаемое дерево
    СтрокаСсылкиЭлементаВтзИсточник = тзИсточник.Найти(СсылкаВладельца, «СсылкаЭлемента»);
    Если СтрокаСсылкиЭлементаВтзИсточник <> Неопределено тогда
    НоваяСтрокаДерева = ДеревоОбъектаСПодчиненными.Строки.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаДерева, СтрокаСсылкиЭлементаВтзИсточник);
    КонецЕсли;
    
    // Поиск подчиненных строк
    мПодчиненныеСтрокиВтзИсточник = тзИсточник.НайтиСтроки(Новый Структура(«СсылкаВладельца», СсылкаВладельца));
    Для каждого ТекПодчиненнаяСтрока из мПодчиненныеСтрокиВтзИсточник Цикл
    СсылкаПодчиненногоЭлемента = ТекПодчиненнаяСтрока.СсылкаЭлемента;
    СформироватьДеревоОбъектаСПодчиненными(НоваяСтрокаДерева, СсылкаПодчиненногоЭлемента, тзИсточник);
    КонецЦикла;
    
    КонецПроцедуры

    Показать

    Reply
  6. Steelvan

    Пример получения дерева. В качестве группирующего поля для дерева используется СсылкаВладельца. На порядок быстрее чем &ВИерархии. Работает без заранее определенного количества уровней.

    Reply
  7. CherAl

    А такой вариант?

    Родитель = Объект.СсылкаНаОбъект.Родитель;
    Пока Родитель <> Справочники[Родитель.метаданные().Имя].ПустаяСсылка() Цикл
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = (Строка(Родитель));
    Сообщение.Сообщить();
    Родитель = Родитель.Родитель;
    КонецЦикла;
    
    Reply
  8. insurgut

    Запрос выдает дважды сам элемент, так что хотя бы так:

    ВЫБРАТЬ
    Склады.Ссылка.Ссылка КАК Родитель
    ИЗ
    Справочник.Склады КАК Склады
    ГДЕ
    Склады.Ссылка = &МойСклад
    ИТОГИ ПО
    Родитель ТОЛЬКО ИЕРАРХИЯ
    Reply
  9. insurgut

    (8) CherAl, приведу простой пример, в обработке заполняется 100 000 строк. При заполнении было условие «Если НЕ РезультатЗапроса.Номенклатура.ЭтоГруппа Тогда»… В итоге обработка открывалась несколько минут. Если в запросе сразу поставить это условие — то секунды.

    Reply
  10. json

    (10) insurgut, согласен. Но в статье я указал, что метод «подходит для небольших по размеру иерархических справочников.»

    Reply
  11. json

    (8) CherAl, пусть в справочнике 100 элементов и 5 уровней иерархии. Сколько будет обращений к базе с Вашим вариантом?

    Reply
  12. json

    (9) insurgut, это просто заготовка. Никто не запрещает накладывать фильтры

    Reply
  13. json

    (3) Lapitskiy, иногда задачи, которые дают на собеседовании пригождаются в реальной практике, а иногда на собеседовании дают задачки из практики

    Reply
  14. TODD22

    (14)

    иногда задачи, которые дают на собеседовании пригождаются в реальной практике, а иногда на собеседовании дают задачки из практики

    Нужно оценивать общий уровень а не умение решать одну задачу которая «иногда пригождается». Для всего с приставкой «иногда» есть гугл.

    Reply
  15. json

    Всем большое спасибо за критику.

    Может ли кто-нибудь подсказать вариант, чтобы данная задача решалась исключительно средствами запросов с возможностью помещения результата во временную таблицу. Например, это было бы очень полезно для формирования отчетов на СКД.

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

    Reply
  16. ildarovich
    Reply
  17. json

    (17) ildarovich, интересный прием. В виде запроса идея намного прозрачнее. Большой плюс, что обращение к самому справочнику только в самом первом запросе, остальное — переливание между временными таблицами.

    Reply
  18. Smilk

    Вот функция которая возвращает текст самостоятельного запроса, или для вставки в другой запрос. Запрос принимает массив ссылок или врем таблицу с ссылками на элементы справочников, добавляет элементы и их родителей в результирующую таблицу. Уровень иерархии так же можно выбрать. Функцию не тестил…

    //berezin p.u 25.02.19
    Функция ПолучитьТекстЗапроса_Услуги_и_их_Родители(ИмяСправочника=»», УровеньВложенности = 2, ИмяВремТаблицыИсточникаСсылок=»», ДляИспользованияВДругомЗапросе = Ложь, МассивСсылок = Неопределено) Экспорт
    
    ВТ = «ВТ»;
    Если ЗначениеЗаполнено(ИмяВремТаблицыИсточникаСсылок) Тогда
    ВТ = ИмяВремТаблицыИсточникаСсылок;
    КонецЕсли;
    
    Начало = «ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ ИЗ Справочник.Номенклатура
    |ГДЕ Ссылка В(&МассивСсылок);»+Символы.ПС;
    
    Середина = «ВЫБРАТЬ » +ВТ+ «#1.Ссылка ПОМЕСТИТЬ » +ВТ+»#2 ИЗ » +ВТ+»#1
    |ОБЪЕДИНИТЬ
    |ВЫБРАТЬ Родитель ИЗ » +ВТ+»#1
    |ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура ПО » +ВТ+»#1.Ссылка = Номенклатура.Ссылка
    |ГДЕ Родитель <> ЗНАЧЕНИЕ(СПравочник.Номенклатура.ПустаяСсылка);»+Символы.ПС;
    
    УничтожениеТаблицы = «УНИЧТОЖИТЬ «+ВТ+»#1;»+Символы.ПС;
    
    //*****************************************************************************
    Если ЗначениеЗаполнено(ИмяСправочника) Тогда
    Начало   = СтрЗаменить(Начало,   «Номенклатура», ИмяСправочника);
    Середина = СтрЗаменить(Середина, «Номенклатура», ИмяСправочника);
    КонецЕсли;
    //*****************************************************************************
    Запрос = Новый Запрос();
    
    Если ДляИспользованияВДругомЗапросе Тогда
    Сч = 0;
    Иначе
    Массив = ?(ТипЗнч(МассивСсылок)=Тип(«Массив»), МассивСсылок, Новый Массив);
    Запрос.Текст = Начало;
    Запрос.УстановитьПараметр(«МассивСсылок», Массив);
    Сч = 1;
    КонецЕсли;
    
    Пока Сч < УровеньВложенности Цикл
    
    Если Не ДляИспользованияВДругомЗапросе И Сч = УровеньВложенности Тогда
    СтрокаПоиска = «ПОМЕСТИТЬ «+ВТ+(Сч+1);
    Середина = СтрЗаменить(Середина, СтрокаПоиска, «»);
    КонецЕсли;
    
    Запрос.Текст = Запрос.Текст + СтрЗаменить(СтрЗаменить(Середина, «#1», ?(Сч=0, «», Сч)), «#2», Сч+1);
    
    Если Сч > 0 Тогда
    Запрос.Текст = Запрос.Текст + СтрЗаменить(УничтожениеТаблицы, «#1», Сч);
    КонецЕсли;
    
    Сч = Сч+1;
    
    КонецЦикла;
    
    
    Возврат Запрос.Текст;
    
    
    КонецФункции
    

    Показать

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *