Принципы ООП в 1С (pattern Iterator)




Принцип обмена данными из 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='\

34 Comments

  1. PerlAmutor

    Под пиво покатит.

    На продакшене, увидев подобное я бы погрузился в древнерусскую тоску.

    Практически новый метод обфусцировать код. Спасибо за труды.

    Reply
  2. lazarenko

    (1)

    я бы погрузился в древнерусскую тоску.

    почему? )))

    Reply
  3. Synoecium
    Ок написали цикл и все хорошо, через какое-то время появляется необходимость в алгоритме получить номер строки (элемента если у нас массив)

    используйте тогда метод Индекс(), он почти во всех коллекциях есть

    Reply
  4. Synoecium
    В первом примере, на какие грабли мы можем наступить, ну к примеру у нас тело цикла достаточно большое, где-то в середине мы (или другой разработчик) пишем Продолжить;, и все, нам эта строка поломала всю логику.

    а тут можно просто инкрементировать счетчик в самом начале цикла — просто начинать не с 0, а с -1, либо уменьшать на 1 при получении элемента по индексу

    Reply
  5. lazarenko

    (3) ну как же, в самой очевидной коллекции — массив, нету.

    Reply
  6. Synoecium

    (5) есть метод, подходящий для массива уникальных значений: Найти(<Значение>)

    Reply
  7. lazarenko

    (6) и Вы считаете это не костыль?

    Reply
  8. Synoecium

    (7) костыль, по сути это поиск по значению, что медленнее обращения к переменной с индексом. Не умаляя достоинств вашего решения, я вспомнил пару решений, не описанных в статье, как без него обойтись.

    Reply
  9. Vladimir Litvinenko

    (2) Потому что мы не привыкли мыслить концепциями ООП и платформа не дает для этого удобных инструментов. Применение ООП фактически идет против сложившейся культуры разработки и, как правило, избегается. По той же причине последние версии БСП кажутся сложными. Как Вы замечали в предыдущих публикациях, там ряд подходов ООП реализован, но с помощью тех возможностей процедурного программирования, которые дает платформа. Поэтому код действительно запутан, а в для реализации позднего связывания часто применяется оператор «Выполнить», усложняющий отладку.

    Публикации на самом деле интересные. Применение паттернов в 1С скорее всего усложнит код в случае решения простых задач. Но в случае решения сложных задач или написании библиотек может помочь и сделать решение более гибким. Или даже упростить решение. Непривычность кода тогда можно преодолеть через его документирование и комментирование.

    Надеюсь продолжите публикации на эту тему.

    Reply
  10. brr

    (9) Может разработчики 1С посмотрят на творящийся ужас и скажут: «давай дадим им ООП»

    Reply
  11. sommid

    минус в том, что это не получится использовать везде, т.к. &НаКлиенте в УФ это не взлетит.. и там все равно будете писать как обычно, и только на сервере продвинуто )

    а так идея интересная

    вопрос еще в накладных расходах на лишний объект

    Reply
  12. Сурикат

    Подскажите, а зачем нам такие накрутки с имплементация , наследованием…?

    Ведь на самом деле мы договариваемся, что наш класс должен содержать определенные методы и поля.

    Метод имплементация проверяет, что соглашения выполнены. Но ведь и платформа это тоже проверит, когда не найдет нужный метод и вызовет исключение.

    На мой взгляд наша цель дополнительно зафиксировать соглашения, что в красивом виде платформа сделать не позволяет.

    И подобный стиль написания кода тоже.

    Но статьи хорошие

    Спасибо вам!

    Reply
  13. lazarenko

    (11) согласен

    Reply
  14. lazarenko

    (12) Платформа это сделает уже при выполнении, скажет не внятное что-то типа «метод объекта не обнаружен», тут мы себя защищаем от такого и приводим к одному типу «интерфейс».

    Вот два примера

    1. Если мы заходим например динамически подсовывать обработки, например как в статье про observer (https://infostart.ru/public/858073) мы по хорошему тут же должны проверить сопоставима обработка с интерфейсом или нет.

    2. Это удобно для случаев когда нам нужно привести к одному типу, например как в статье про pattern Chain of responsibility (https://infostart.ru/public/851798), там Handler приведен к типу интерфейса. К тому же если рассматривать текущий пример, допустим мы уберем имплементацию и напишем 2 курсора для массива и ТЗ, в одном из курсоров не захотим/забудем реализовать какой-то объект, например сортировку, т.к. он конкретно в нашей задаче не нужен, в целом не страшно, но уже теряется унификация, получится как сейчас у платформы, у какой-то коллекции есть метод Индекс() у какой-то нет. А вообще это ж пример как можно, понятно что можно и без интерфейса, можно вообще и без обработок.

    Reply
  15. boln

    Всячески приветствую исследования автора в применении паттернов GoF к 1С. Сам имею такие наклонности 🙂

    Но в данном кейсе, ИМХО, все же есть момент «паттерн ради паттерна». Выгода для программиста весьма туманна, согласен с комментариями. Все-таки паттерны изобретаются не ради самих паттернов.

    Reply
  16. Vladimir Litvinenko

    (10) Кажется, что сейчас у них в приоритете EDT и развитие GUI. Еще судя по партнерскому форуму из планов не убирают переход на микросервисную архитектуру 8.4 и переписывания кучи кода серверной части на Java. Из этого направления пока что только RAS, RAC и система взаимодействия живыми вышли ))

    На развитие самого языка скорее всего ресурсов не хватает, и оно идет в основном по пути переноса ряда функций БСП на уровень платформы. Да и появление ООП в платформе было бы спорным решением для российского рынка. Текущие позиции на рынке держатся во многом за счет простоты языка. Если только борьба за международный рынок заставит над развитием языка в этом направлении поработать, как сейчас заставляет работать над EDT.

    Reply
  17. nicxxx

    Расскажите, кто-нибудь, автору про запятые. «т.е. из классической схемы убран агрегатор в квази ООП он кажется избыточным.»

    Reply
  18. boln

    (16)

    Текущие позиции на рынке держатся во многом за счет простоты языка.

    Крайне сомнительно. На 1С программируют уже давно не «домохозяйки».

    Reply
  19. nomadon

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

    Reply
  20. CyberCerber

    Многих, наверное, пугает, вот эта страшная строка:

    Курсор = Обработки.КурсорТаблицаЗначений.Создать().Конструктор(Таб).Имплементация(«КурсорИнтерфейс»);

    А нельзя, может, и с потерей принципов, сделать такую реализацию?

    Курсор = Курсоры.Создать(Таб);

    Здесь Курсоры — это общий модуль. В метод Создать передаем нужную коллекцию, и, в зависимости от типа, возвращается та или иная обработка со своими реализациями методов работы с коллекциями.

    Но у данной методики остается такой же недостаток, а именно отсутствие контекстной подсказки по методам. Решить это можно следующим образом:

    Курсор = Обработки.КурсорИнтерфейс.Создать();
    Курсор.Конструктор(Таб);

    Тогда в переменной Курсор будет Объект.Обработка и все экспортные методы будут подсказываться.

    Реализацию для каждого типа коллекций придется сделать не через наследование, а через агрегацию. Т.е. при вызове метода Конструктор в зависимости от типа переданной коллекции, в реквизит обработки, например, Реализация кладется та или иная обработка. И все методы обработки КурсорИнтерфейс будут выглядит так:

    Функция Следующий() Экспорт
    Возврат Реализация.Следующий();
    КонецФункции;

    Т.е. обработка КурсорИнтерфейс будет что-то типа абстрактного класса с виртуальными методами.

    P.S. Автор, хочу выразить благодарность за статьи по такой хорошей тематике и пожелать развития темы.

    Reply
  21. brr

    (16)Простота языка уже мешает, сложность конфигураций выросла, язык должен соответствовать.

    Reply
  22. brr

    (20)Хехе, паттерн фабрика

    Reply
  23. starik-2005

    Норм.

    Reply
  24. l1ike

    (12) Чего только люди не придумают, чтобы не писать тесты.

    Reply
  25. pun4er

    Интересно, спасибо

    Reply
  26. starik-2005

    (24) в Боинге тесты заменили ревизией кода — повысили производительность труда в 8 раз (Макконнелл, «Совершенный код»).

    Reply
  27. l1ike

    (26) А если на хаскеле писать, то если скомпилировалось — значит нет ошибок )))

    Reply
  28. starik-2005

    (27)

    значит нет ошибок

    Не факт. Если нет ошибок в типах, то это не значит, что их вообще нет:

    Отличительная черта языка — серьёзное отношение к типизации; во многом в связи с этим язык назван в честь исследователя теории типов и изобретателя комбинаторной логики Хаскелла Карри.
    Reply
  29. l1ike

    (28) Остальные ошибки, как правило, проще отлавливаются приемочными тестами, либо при сдаче работ заказчику.

    Reply
  30. starik-2005

    (29)

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

    Не совсем так. В ходе исследований, на которые ссылается Макконнелл, выяснилось, что тесты обычно выявляют не более 30% ошибок, при этом ревью кода выявляет до 70%, что сопоставимо с парным программированием. В тестировании пользователями бета-версий выявляется до 97% ошибок, остальные 3% ошибок могут достаточно долго жить в проекте и не быть обнаруженными, что, понятное дело, влечет за собой риски от правильности результатов работы системы до уровня защищенности данных от атак хаккеров.

    Reply
  31. l1ike

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

    Reply
  32. lazarenko

    (14)

    какой-то объект

    какой-то метод*

    Reply
  33. lazarenko

    (17) Исправлюсь

    Reply
  34. babys

    (21) Ну тут надо ориентироваться скорее на то, что язык прикладной и насыщать его бизнес функциями как VB for Office, а не тупо гнать в универсальность, не важно с ООП или без оного.

    А как пример функциональной простоты С— можно глянуть 🙂

    Reply

Leave a Comment

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