├── .gitignore
├── LICENSE
├── README.md
├── admin
├── applyError.php
├── cli.php
├── controller.php
├── createScenario.php
├── detail.php
├── log.php
├── main.php
└── menu.php
├── composer.json
├── data
└── scenarioTemplate.tpl
├── description.ru
├── docs
├── builders.md
├── cli.md
├── img
│ ├── cli_action_help.png
│ ├── cli_apply_all.png
│ ├── cli_apply_hash.png
│ ├── cli_apply_skip.png
│ ├── cli_create.png
│ ├── cli_create_full.png
│ ├── cli_create_quick.png
│ ├── cli_help.png
│ ├── cli_history_count.png
│ ├── cli_history_last.png
│ ├── cli_list.jpg
│ ├── cli_list.png
│ ├── cli_rollback.jpg
│ ├── cli_rollback_count.png
│ ├── cli_rollback_hash.png
│ ├── cli_rollback_last.png
│ ├── cli_rollback_up_to.png
│ ├── create_scenario.png
│ ├── install_market.jpg
│ ├── main.jpg
│ ├── project_state.png
│ ├── settings_page.jpg
│ └── update_scenario.jpg
├── scripts.md
├── setup.md
└── web.md
├── include.php
├── install
├── admin
│ └── ws_reducemigrations.php
├── db
│ ├── install.sql
│ └── uninstall.sql
├── form.php
├── index.php
├── tools
│ └── migrate
├── uninstall.php
└── version.php
├── lang
├── en
│ ├── admin.php
│ ├── info.php
│ ├── menu.php
│ ├── setup.php
│ ├── tests.php
│ └── uninstall.php
└── ru
│ ├── admin.php
│ ├── info.php
│ ├── menu.php
│ ├── setup.php
│ ├── tests.php
│ └── uninstall.php
├── lib
├── applyresult.php
├── builder
│ ├── agentbuilder.php
│ ├── builderexception.php
│ ├── entity
│ │ ├── agent.php
│ │ ├── base.php
│ │ ├── enumvariant.php
│ │ ├── eventmessage.php
│ │ ├── eventtype.php
│ │ ├── fieldwrapper.php
│ │ ├── form.php
│ │ ├── formanswer.php
│ │ ├── formfield.php
│ │ ├── formstatus.php
│ │ ├── highloadblock.php
│ │ ├── iblock.php
│ │ ├── iblocktype.php
│ │ ├── property.php
│ │ ├── table.php
│ │ └── userfield.php
│ ├── eventsbuilder.php
│ ├── formbuilder.php
│ ├── highloadblockbuilder.php
│ ├── iblockbuilder.php
│ ├── iblockpointer.php
│ ├── tablebuilder.php
│ └── traits
│ │ ├── containuserfieldstrait.php
│ │ └── operateuserfieldentitytrait.php
├── collection
│ └── migrationcollection.php
├── console
│ ├── command
│ │ ├── applycommand.php
│ │ ├── basecommand.php
│ │ ├── createscenariocommand.php
│ │ ├── helpcommand.php
│ │ ├── history.php
│ │ ├── listcommand.php
│ │ └── rollbackcommand.php
│ ├── console.php
│ ├── consoleexception.php
│ ├── formatter
│ │ ├── output.php
│ │ └── table.php
│ ├── pear
│ │ └── consoletable.php
│ ├── runtimecounter.php
│ └── runtimefixcounter.php
├── dumbmessageoutput.php
├── entities
│ ├── appliedchangeslog.php
│ ├── appliedchangeslogmodel.php
│ ├── baseentity.php
│ ├── eventmessage.php
│ ├── setuplog.php
│ └── setuplogmodel.php
├── exceptions
│ ├── multipleequalhashexception.php
│ └── nothingtoapplyexception.php
├── factories
│ └── datetimefactory.php
├── localization.php
├── messageoutputinterface.php
├── migrationapplier.php
├── migrationrollback.php
├── module.php
├── moduleoptions.php
├── options.php
├── scenario
│ ├── exceptions
│ │ ├── applyscenarioexception.php
│ │ └── skipscenarioexception.php
│ └── scriptscenario.php
├── tests
│ ├── abstractcase.php
│ ├── cases
│ │ ├── agentbuildercase.php
│ │ ├── errorexception.php
│ │ ├── eventsbuildercase.php
│ │ ├── formbuildercase.php
│ │ ├── highloadblockbuildercase.php
│ │ ├── iblockbuildercase.php
│ │ └── tablebuildercase.php
│ ├── result.php
│ └── starter.php
├── timeformatter.php
└── timer.php
├── options.php
├── prolog.php
├── scripts
└── install.php
└── updater.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor/
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 WorkSolutions
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Модуль миграций 2.0
2 | ===============
3 |
4 | Модуль миграций для CMS "1С-Битрикс" – быстрые и стабильные обновления баз данных проекта.
5 |
6 | Известно, что с развитием проекта изменению подлежат не только исходный код и алгоритмы бизнес логики, но так же добавляются или удаляются новые сущности или их поля. Для упрощения изменений и последующих обновлений данных и предназначен данный модуль.
7 |
8 | ## Возможности
9 |
10 | * Составление сценариев миграций с помощью специальных ```билдеров```, специально предназначенных для этих нужд. Таким образом создание новых сущностей данных будет происходить не труднее чем через административный интерфейс;
11 | * Актуализация данных. Бавают случаи когда данные нужно "подправить" не меняя структуры - миграции подходят для этого как нельзя кстати. Сценарий будет запущен один раз и для всех площадок;
12 | * Работа через командную строку. Обновление можно выполнять как вместе с обновлением исходного кода, так и использовать специальные инструменты систем версионирования - запуска скриптов после обновления.
13 |
14 | ## Преимущества
15 |
16 | * Стабильность. Сценарии миграций данных составляются и отлаживаются командой.
17 | * Удобство. Модуль обладает широким спектром функционала для манипулирования миграциями.
18 | * Информативность. Удобный вывод списков миграций при работе через консоль.
19 | * Предсказуемость. Можно указывать примерное время выполнения миграций – это будет спобоствовать правильному принятию решения при обновлении.
20 |
21 | ## Как это работает?
22 |
23 | Простейшая схема работы команды над проектом выглядит следующим образом:
24 |
25 | 
26 |
27 | Есть локальные площадки програмистов и есть сервера которые доступны "извне" через Интернет. Каждая площадка имеет отдельную базу данных. Базы данных площадок отличаются только наполнением, но схемы (таблицы, поля, инфоблоки и т.д.) данных одинаковы.
28 |
29 | Процесс изменения схемы данных (либо манипуляции над данными) которые нужны для каждой площадки следующий:
30 |
31 | 1. Необходимо сделать изменения в схеме данных на проекте. К примеру добавить поле в одну из сущностей, которое будет использоваться новой функцией.
32 | 2. Разработчик создает миграцию добавления нового поля. Миграцией будет являться файл (класс php), определенного формата, в котором необходимо написать сценарии обновления (добавления поля) и отката (удаления поля) на случай неудачного применения обновления или отката версии проекта на предыдущую.
33 | 3. Запускает миграцию на локальной копии проекта, отлаживает применение и откат миграции.
34 | 4. Регистрирует миграцию в системе версионирования. Так исходный код для запуска миграции распространится по всем платформам.
35 | 5. Каждая площадка получившая исменения исходного кода запускает обновления миграций.
36 |
37 | Таким образом новое поле в базу данных добавляется для всех площадок получивших изменения одинаковым образом.
38 |
39 | ## Что дальше?
40 |
41 | * [Устанавливаем модуль](docs/setup.md)
42 | * [Создаем сценарии миграций](docs/scripts.md)
43 | * [Используем построители сущностей](docs/builders.md)
44 | * [Работаем с готовыми сценариями миграций через командную строку](docs/cli.md)
45 | * [Миграции в административном интерфейсе](docs/web.md)
46 |
--------------------------------------------------------------------------------
/admin/applyError.php:
--------------------------------------------------------------------------------
1 | array('=id' => $id)));
7 | if (!$model) {
8 | require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_js.php");
9 | ShowError($localization->message('error.modelNotExists', array(
10 | ':id:' => $id
11 | )));
12 | require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_admin_js.php");
13 | }
14 | /** @var CMain $APPLICATION */
15 |
16 | require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_js.php");
17 | ?>
18 |
19 |
20 |
21 | =$localization->message('message')?>
22 |
23 |
24 | getName(), $model->getErrorMessage()))?>
25 |
26 |
27 |
28 |
43 | printLine("Migrations module for CMS Bitrix. Worksolutions company https://worksolutions.ru \n");
24 | };
25 |
26 | $getShowProgress = function () use ($console) {
27 | $counter = new \WS\ReduceMigrations\Console\RuntimeCounter();
28 | return function ($data, $type) use ($console, $counter) {
29 | if ($type == 'setCount') {
30 | $counter->migrationCount = $data;
31 | $counter->start = microtime(true);
32 | }
33 | if ($type == 'start') {
34 | $counter->migrationNumber++;
35 | $console->printLine(sprintf(
36 | '%s (%s/%s)',
37 | $console->colorize($data['name'], Console::OUTPUT_PROGRESS),
38 | $counter->migrationNumber, $counter->migrationCount
39 | ));
40 | }
41 | if ($type == 'end') {
42 | /**@var \WS\ReduceMigrations\Entities\AppliedChangesLogModel $log */
43 | $log = $data['log'];
44 | $time = round($data['time'], 2);
45 | $message = '';
46 | if (!empty($data['error'])) {
47 | $message .= ' ' . $data['error'];
48 | }
49 | $overallTime = round(microtime(true) - $counter->start, 2);
50 | $message .= sprintf(' - %s (%s)', $console->formatTime($time), $console->formatTime($overallTime));
51 | if ($log->isSkipped()) {
52 | $message = ' - skipped';
53 | }
54 | $console->printLine($message, $log->isFailed() ? Console::OUTPUT_ERROR : Console::OUTPUT_SUCCESS);
55 | $console->printLine("");
56 | }
57 | };
58 | };
59 | try {
60 | $console->printLine('');
61 | $command = $console->getCommand();
62 | $command->execute($getShowProgress());
63 | $fCompanyLabel();
64 | } catch (\WS\ReduceMigrations\Console\ConsoleException $e) {
65 | $console->printLine($e->getMessage());
66 | $fCompanyLabel();
67 | }
68 |
--------------------------------------------------------------------------------
/admin/controller.php:
--------------------------------------------------------------------------------
1 | isAdmin()) {
7 | return ;
8 | }
9 |
10 | CModule::IncludeModule('ws.reducemigrations');
11 |
12 | $request = $_REQUEST;
13 | $action = $request['q'];
14 | $fAction = function ($file) use ($action) {
15 | global
16 | $USER, $DB, $APPLICATION, $adminPage, $adminMenu, $adminChain;
17 | $localization = \WS\ReduceMigrations\Module::getInstance()->getLocalization('admin')->fork($action);
18 | include $file;
19 | };
20 |
21 | $actionFile = __DIR__.DIRECTORY_SEPARATOR.$request['q'].'.php';
22 | if (file_exists($actionFile)) {
23 | $fAction($actionFile);
24 | } else {
25 | /* @var $APPLICATION CMain */
26 | $APPLICATION->ThrowException("Action `$actionFile` not exists");
27 | }
28 | require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_admin_after.php");
29 | ?>
--------------------------------------------------------------------------------
/admin/createScenario.php:
--------------------------------------------------------------------------------
1 | getContext()->getRequest();
8 | $fileName = '';
9 | $hasError = false;
10 | if ($_POST['save'] != "" && $_POST['name']) {
11 | $name = trim($request->get('name'));
12 | $priority = trim($request->get('priority'));
13 | $time = trim($request->get('time'));
14 |
15 | try {
16 | $fileName = $module->createScenario($name, $priority, $time);
17 | } catch (Exception $e) {
18 | $hasError = true;
19 | }
20 | }
21 |
22 | if ($_POST['save'] != "" && !$_POST['name']) {
23 | $hasError = true;
24 | }
25 | $APPLICATION->SetTitle($localization->getDataByPath('title'));
26 | require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_after.php");
27 |
28 | $fileName && CAdminMessage::ShowNote($localization->message('path-to-file', array('#path#' => $fileName)));
29 | $hasError && CAdminMessage::ShowMessage(array("MESSAGE" => $localization->message('save-file-error'), "TYPE" => "ERROR"));
30 | ?>
60 | SetTitle($localization->getDataByPath('title'));
13 | $sTableID = "ws_reducemigrations_log_table";
14 | $oSort = new CAdminSorting($sTableID, "date", "asc");
15 | $lAdmin = new CAdminList($sTableID, $oSort);
16 |
17 | $arHeaders = array(
18 | array("id" => "updateDate", "content" => $localization->getDataByPath('fields.updateDate'), "default"=>true),
19 | array("id" => "description", "content" => $localization->getDataByPath('fields.description'), "default" => true),
20 | array("id" => "hash", "content" => $localization->getDataByPath('fields.hash'), "default" => true),
21 | array("id" => "dispatcher", "content" => $localization->getDataByPath('fields.dispatcher'), "default" => true)
22 | );
23 | $lAdmin->AddHeaders($arHeaders);
24 |
25 | $models = AppliedChangesLogModel::find(array(
26 | 'limit' => 500,
27 | 'order' => array(
28 | 'groupLabel' => 'desc'
29 | )
30 | ));
31 |
32 | $rowsData = array();
33 | array_walk($models, function (AppliedChangesLogModel $model) use (& $rowsData) {
34 | $row = & $rowsData[$model->getGroupLabel()];
35 | if(!$row) {
36 | $row = array(
37 | 'label' => $model->getGroupLabel(),
38 | 'updateDate' => $model->getDate()->format('d.m.Y H:i:s'),
39 | 'hash' => $model->getHash(),
40 | 'dispatcher' => $model->getSetupLog() ? $model->getSetupLog()->shortUserInfo() : ''
41 | );
42 | }
43 | $row['description'] = $row['description'] ? implode(" ", array($row['description'], $model->getName())) : $model->getName();
44 | if ($model->isFailed()) {
45 | $row['error'][] = array(
46 | 'data' => array('message' => $model->getErrorMessage()),
47 | 'id' => $model->getId()
48 | );
49 | }
50 | });
51 |
52 | $rsData = new CAdminResult(null, $sTableID);
53 | $rsData->InitFromArray($rowsData);
54 | $rsData->NavStart();
55 |
56 | $lAdmin->NavText($rsData->GetNavPrint($localization->getDataByPath('messages.pages')));
57 | while ($rowData = $rsData->NavNext()) {
58 | $row = $lAdmin->AddRow($rowData['label'], $rowData);
59 | $description = $rowData['description'];
60 | if ($rowData['error']) {
61 | $description = array();
62 | foreach ($rowData['error'] as $rowErrorData) {
63 | $description[] = ''.$rowErrorData['data']['message'].' ';
64 | }
65 | $description = implode(' ', $description);
66 | }
67 |
68 | $row->AddViewField('description', $description);
69 | $row->AddActions(array(
70 | array(
71 | "ICON" => "view",
72 | "TEXT" => $localization->message('messages.view'),
73 | "ACTION" => $lAdmin->ActionRedirect("ws_reducemigrations.php?q=detail&label=".$rowData['label'].'&type=applied'. '&lang=' . LANGUAGE_ID),
74 | "DEFAULT" => true
75 | )
76 | ));
77 | }
78 | if ($_REQUEST["mode"] == "list") {
79 | require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_js.php");
80 | } else {
81 | require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_after.php");
82 | }
83 |
84 | $lAdmin->CheckListMode();
85 | $lAdmin->DisplayList();
86 | ?>
87 |
103 | isAdmin()) {
4 | return array();
5 | }
6 | CModule::IncludeModule('ws.reducemigrations');
7 | $loc = \WS\ReduceMigrations\Module::getInstance()->getLocalization('menu');
8 | $inputUri = '/bitrix/admin/ws_reducemigrations.php?lang=' . LANGUAGE_ID . '&q=';
9 | return array(
10 | array(
11 | 'parent_menu' => 'global_menu_settings',
12 | 'sort' => 500,
13 | 'text' => $loc->getDataByPath('title'),
14 | 'title' => $loc->getDataByPath('title'),
15 | 'module_id' => 'ws_reducemigrations',
16 | 'icon' => '',
17 | 'items_id' => 'ws_reducemigrations_menu',
18 | 'items' => array(
19 | array(
20 | 'text' => $loc->getDataByPath('apply'),
21 | 'url' => $inputUri.'main',
22 | ),
23 | array(
24 | 'text' => $loc->getDataByPath('createScenario'),
25 | 'url' => $inputUri.'createScenario'
26 | ),
27 | array(
28 | 'text' => $loc->getDataByPath('log'),
29 | 'url' => $inputUri.'log',
30 | 'more_url' => array($inputUri.'detail')
31 | )
32 | )
33 | )
34 | );
35 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "worksolutions/bitrix-reduce-migrations",
3 | "description": "Bitrix module for migrations",
4 | "type": "bitrix-module",
5 | "license": "MIT",
6 | "support": {
7 | "issues": "https://github.com/worksolutions/bitrix-reduce-migrations/issues",
8 | "source": "https://github.com/worksolutions/bitrix-reduce-migrations"
9 | },
10 | "authors": [
11 | {
12 | "name": "Igor Pomiluyko",
13 | "email": "pomiluyko@worksolutions.ru"
14 | }
15 | ],
16 | "extra": {
17 | "installer-name": "ws.reducemigrations"
18 | },
19 | "require": {
20 | "php": ">=5.3.0",
21 | "composer/installers": "~1"
22 | },
23 | "scripts": {
24 | "post-install-cmd": [
25 | "php scripts/install.php"
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/data/scenarioTemplate.tpl:
--------------------------------------------------------------------------------
1 | printer() method.
39 | **/
40 | public function commit() {
41 | // my code
42 | }
43 |
44 | /**
45 | * Write action by rollback scenario. Use method `getData` for getting commit saved data.
46 | * For printing info into console use object from $this->printer() method.
47 | **/
48 | public function rollback() {
49 | // my code
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/description.ru:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/description.ru
--------------------------------------------------------------------------------
/docs/cli.md:
--------------------------------------------------------------------------------
1 | ##### [Главная страница](../README.md)
2 |
3 | ### Интерфейс командной строки
4 |
5 | Основным требованием к любому функуионалу должно быть требование удобства использования. Это означает что:
6 |
7 | 1. Все действия должны быть интуитивно понятны;
8 | 2. Работая с функционалом нет нужды обращаться к документации.
9 |
10 | Мы постарались сделать наиболее удобным использование модуля как при написании миграции так и при работе с очередью уже готовых миграций.
11 |
12 | Основной интерфейс модуля - терминал. При использовании этого подхода есть неоспоримые приимущества:
13 |
14 | 1. Время выполнения миграций не ограничено таймаутом сервера;
15 | 2. Возможность автоматизировать обновление миграций совместно с обновлением кода. К примеру, можно интегрировать механизм обновления миграций
16 | при помощи функционала перехватчиков `СУРВ Git`
17 |
18 | #### Начало работ
19 |
20 | Файл для работы с миграцими через интерфейс командной строки расположен по пути `bitrix/tools/migrate`, таким образом запускать миграции через командную строку всегда нужно через этот файл.
21 |
22 | Пример:
23 |
24 | ```sh
25 | $ php bitrix/tools/migrate
26 | ```
27 |
28 | Отображает список доступных действий
29 |
30 | 
31 |
32 | Можно получить более подробную информацию по каждому действию набрав ```--help``` после имени действия, к примеру:
33 | ```sh
34 | $ php bitrix/tools/migrate create --help
35 | ```
36 |
37 | 
38 | Выводится простая, но в то же время содержательная справка по параметрам действия и их использованию
39 |
40 | #### Список миграций доступных для обновления
41 |
42 | ```sh
43 | $ php bitrix/tools/migrate list
44 | ```
45 |
46 | 
47 |
48 | Миграции в списке группируются по приоритету, именно в таком порядке они будут обновляться. Так же выводится суммарное время обновления согласно пустановленным периодам в сценариях. Это позволяет правильно ориентироваться по времени при обновлении.
49 |
50 | #### Создание миграции
51 |
52 | Интерактивный способ
53 |
54 | ```sh
55 | $ php bitrix/tools/migrate create
56 | ```
57 |
58 | 
59 |
60 | Быстрый способ
61 |
62 | 
63 |
64 | #### Применение подготовленных миграций
65 |
66 | Запуск миграции по хешу.
67 |
68 | ```sh
69 | $ php bitrix/tools/migrate apply a5468917
70 | ```
71 |
72 | 
73 |
74 | Из списка подготовленных миграций выбирается та, которая соответствует подстроке хеша.
75 |
76 | Запуск всех миграций.
77 |
78 | ```sh
79 | $ php bitrix/tools/migrate apply -f
80 | ```
81 |
82 | 
83 |
84 | Флаг ```-f``` указывает на то что применение миграций начинать без запроса на подтверждение.
85 |
86 | Запуск всех миграций.
87 |
88 | ```sh
89 | $ php bitrix/tools/migrate apply -f --skip-optional
90 | ```
91 |
92 | 
93 |
94 | Параметр ```--skip-optional``` указывает на пропуск опциональных миграций. Опциональные миграции создаются в основном для актуализаций боевой площадки и занимают продолжительное время, на площадках разработчиков такая актуализация не требуется и при обновлении миграций на площадке разработчика их можно пропустить.
95 |
96 | Подойдет для обработчиков ```git```
97 |
98 | #### История применения миграций
99 |
100 | Все примененные миграции сохраняются в истории.
101 |
102 | Последнее обновление миграций:
103 | ```sh
104 | $ php bitrix/tools/migrate history
105 | ```
106 |
107 | 
108 |
109 | Просмотр истории по количеству последних миграций:
110 | ```sh
111 | $ php bitrix/tools/migrate history
112 | ```
113 |
114 | 
115 |
116 | #### Откат миграций
117 |
118 | Откат миграций производится через метод класса миграций ```rollback```
119 |
120 | Откат последнего обновления:
121 | ```sh
122 | $ php bitrix/tools/migrate rollback
123 | ```
124 |
125 | 
126 |
127 | Откат последнего обновления:
128 | ```sh
129 | $ php bitrix/tools/migrate rollback
130 | ```
131 |
132 | 
133 |
134 | Откат одной миграции по хешу:
135 | ```sh
136 | $ php bitrix/tools/migrate rollback b907dc51
137 | ```
138 |
139 | 
140 |
141 | Откат определенного количества последних миграций:
142 | ```sh
143 | $ php bitrix/tools/migrate rollback --count=2
144 | ```
145 |
146 | 
147 |
148 | Откат всех свежих миграций вплоть до определенной из списка:
149 | Откат определенного количества последних миграций:
150 | ```sh
151 | $ php bitrix/tools/migrate rollback --count=2
152 | ```
153 |
154 | 
155 |
156 | Применение и откат миграций сопровождается списком с перечнем того что будет обновляться или отменяться.
157 |
--------------------------------------------------------------------------------
/docs/img/cli_action_help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_action_help.png
--------------------------------------------------------------------------------
/docs/img/cli_apply_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_apply_all.png
--------------------------------------------------------------------------------
/docs/img/cli_apply_hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_apply_hash.png
--------------------------------------------------------------------------------
/docs/img/cli_apply_skip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_apply_skip.png
--------------------------------------------------------------------------------
/docs/img/cli_create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_create.png
--------------------------------------------------------------------------------
/docs/img/cli_create_full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_create_full.png
--------------------------------------------------------------------------------
/docs/img/cli_create_quick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_create_quick.png
--------------------------------------------------------------------------------
/docs/img/cli_help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_help.png
--------------------------------------------------------------------------------
/docs/img/cli_history_count.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_history_count.png
--------------------------------------------------------------------------------
/docs/img/cli_history_last.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_history_last.png
--------------------------------------------------------------------------------
/docs/img/cli_list.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_list.jpg
--------------------------------------------------------------------------------
/docs/img/cli_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_list.png
--------------------------------------------------------------------------------
/docs/img/cli_rollback.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_rollback.jpg
--------------------------------------------------------------------------------
/docs/img/cli_rollback_count.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_rollback_count.png
--------------------------------------------------------------------------------
/docs/img/cli_rollback_hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_rollback_hash.png
--------------------------------------------------------------------------------
/docs/img/cli_rollback_last.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_rollback_last.png
--------------------------------------------------------------------------------
/docs/img/cli_rollback_up_to.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/cli_rollback_up_to.png
--------------------------------------------------------------------------------
/docs/img/create_scenario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/create_scenario.png
--------------------------------------------------------------------------------
/docs/img/install_market.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/install_market.jpg
--------------------------------------------------------------------------------
/docs/img/main.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/main.jpg
--------------------------------------------------------------------------------
/docs/img/project_state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/project_state.png
--------------------------------------------------------------------------------
/docs/img/settings_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/settings_page.jpg
--------------------------------------------------------------------------------
/docs/img/update_scenario.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/docs/img/update_scenario.jpg
--------------------------------------------------------------------------------
/docs/scripts.md:
--------------------------------------------------------------------------------
1 | ##### [Главная страница](../README.md)
2 |
3 | ## Работа со скриптами миграций
4 |
5 | `Модуль миграций` предоставляет функционал создания "гибкой миграции" путем определения сценария обновления программистом. Также имеется набор классов для быстрого и удобного создания новых данных.
6 |
7 | Файлы миграций создаются в виде PHP классов.
8 |
9 | ### 1. Создание класса сценария миграции
10 |
11 | #### Консоль
12 |
13 | Один из простых способов создания миграйции – через консоль.
14 |
15 | 
16 |
17 | В этом случае необходимо ввести название миграции и приоритет.
18 |
19 | Существуют три варианта проритета миграции:
20 | * ```high (h)``` - высокий, сценарии с этим приоритетом обновляются в первую очередь;
21 | * ```middle (m)``` - средний, такие сценарии выполняются сразу после сценариев с высоким приоритетом;
22 | * ```optional (o)``` - опциональный, могут быть пропушены во время обновления;
23 |
24 | В любом случае приоритет можно сменить в исходном коде миграции после ее создания.
25 |
26 | #### Административный интерфейс
27 |
28 | Создание класса сценария осуществляется из меню `Миграции данных -> Сценарий обновления`, где необходимо ввести название сценария, выбрать приоритет миграции и примерное время выполнения.
29 |
30 | Примерное время выполнения можно установить непосредственно в исходном коде файла миграции.
31 |
32 | ##### Создание сценария. Ввод названия
33 |
34 | 
35 |
36 | Название можно задавать кириллицей. После создания появляется сообщение с информацией о местонахождении файла класса
37 |
38 | 
39 |
40 | ### 2. Редактирование. Определение алгоритма исполнения сценария
41 |
42 | По пути, указанному в сообщении находится следующий класс:
43 |
44 | ##### Сценарий. Редактирование класса
45 |
46 | ```php
47 | createIblock('type_content', 'Новости', function (\WS\ReduceMigrations\Builder\Entity\Iblock $iblock) {
125 | $iblock
126 | ->siteId('s1')
127 | ->sort(100)
128 | ->code('news')
129 | ->groupId(array(
130 | '2' => 'R'
131 | ));
132 | $iblock
133 | ->addProperty('Выводить на главной')
134 | ->code('showOnMain')
135 | ->typeCheckbox()
136 | ->addEnum('да');
137 | });
138 |
139 | ```
140 |
141 | Перечень классов:
142 |
143 | * \WS\ReduceMigrations\Builder\TableBuilder
144 | * \WS\ReduceMigrations\Builder\IblockBuilder
145 | * \WS\ReduceMigrations\Builder\HighLoadBlockBuilder
146 | * \WS\ReduceMigrations\Builder\FormBuilder
147 | * \WS\ReduceMigrations\Builder\EventsBuilder
148 | * \WS\ReduceMigrations\Builder\AgentBuilder
149 |
--------------------------------------------------------------------------------
/docs/setup.md:
--------------------------------------------------------------------------------
1 | ##### [Главная страница](../README.md)
2 |
3 | # Установка и настройка
4 |
5 | ## Composer
6 |
7 | Подключение модуля необходимо проводить находясь в DOCUMENT_ROOT проекта:
8 |
9 | ```
10 | composer require worksolutions/bitrix-reduce-migrations
11 | ```
12 | Команда перенесет файлы модуля в папку `/bitrix/modules/ws.reducemigrations`
13 |
14 | Следующим шагом будет регистрация в битрикс:
15 | ```
16 | composer run-script post-install-cmd -d bitrix/modules/ws.reducemigrations
17 | ```
18 |
19 | Результатом регистрации будут следующие пункты:
20 | 1. Модуль будет зарегистрирован в системе;
21 | 2. Будет создан каталог ```reducemigrations``` в папке на уровне bitrix;
22 | 3. В каталог bitrix/admin переносится файл-контроллер модуля. Для работы модуля через административный интерфейс.
23 | 4. Появится файл ```bitrix/tools/migrate``` для работы миграций через командную строку.
24 |
25 | Также регистрацию модуля можно запустить через страницу "Установленные решения":
26 | ```
27 | http://my-site.ru/bitrix/admin/partner_modules.php?lang=ru
28 | ```
29 |
30 | ## Marketplace
31 |
32 | Для установки в адресную строку браузера, после доменного имени, прописать:
33 | ```
34 | /bitrix/admin/update_system_partner.php?addmodule=ws.reducemigrations
35 | ```
36 |
37 | К примеру:
38 | ```
39 | http://my-site.ru/bitrix/admin/update_system_partner.php?addmodule=ws.reducemigrations
40 | ```
41 |
42 | При установке модуля необходимо определить основные параметры.
43 |
44 | ### Определение параметров при установке модуля
45 |
46 | Для успешной работы модуля необходимо определить каталог, где будут создаваться файлы синхронизации данных.
47 | Каталог сделать должен синхронизироваться системой версионирования проекта.
48 |
49 | 
50 |
51 | ### Настройка модуля
52 |
53 | После установки модуля в настройках можно изменить путь к каталогу, в котором будут храниться миграции
54 | 
55 |
56 | ## Переход с первой версии модуля
57 |
58 | Если на проекте использовалась первая [версия модуля](https://github.com/worksolutions/bitrix-module-migrations), то желательно для нового модуля настроить другой каталог хранения файлов миграций. Это исключит путаницу связанную с одинаковыми по назначению, но не по реализации, модулями. Конечно при этом все члены команды должны осознавать то, что они работают с новой версией миграций данных.
59 |
60 | Таким образом переход к новому модулю можно осуществить в два этапа:
61 |
62 | 1. Установить параллельно новый модуль;
63 | 2. Через время удалить неиспользуемый, в тот момент, когда полностью налажена работа с новым модулем.
64 |
--------------------------------------------------------------------------------
/docs/web.md:
--------------------------------------------------------------------------------
1 | ##### [Главная страница](../README.md)
2 |
3 | ### Административный интерфейс модуля
4 |
5 | Хотя модуль имеет возможность работать через административный интерфейс, рекомендуется использовать его только для чтения данных о миграциях.
6 |
7 | 
8 |
--------------------------------------------------------------------------------
/include.php:
--------------------------------------------------------------------------------
1 | getLocalization('setup');
4 | $options = \WS\ReduceMigrations\Module::getInstance()->getOptions();
5 | $module = \WS\ReduceMigrations\Module::getInstance();
6 |
7 | $errors && CAdminMessage::ShowMessage(
8 | array(
9 | "MESSAGE" => implode(', ', $errors),
10 | "TYPE" => "ERROR",
11 | )
12 | );
13 |
14 | $form = new CAdminForm('ew', array(
15 | array(
16 | 'DIV' => 't1',
17 | 'TAB' => $localization->getDataByPath('tab'),
18 | ),
19 | ));
20 |
21 | echo BeginNote();
22 | echo $localization->getDataByPath('description');
23 | echo EndNote();
24 |
25 | $form->Begin(array(
26 | 'FORM_ACTION' => $APPLICATION->GetCurUri(),
27 | ));
28 | $form->BeginNextFormTab();
29 | $form->AddEditField('data[catalog]', $localization->getDataByPath('fields.catalog'), true, array(), $options->catalogPath ?: '/bitrix/php_interface/reducemigrations');
30 |
31 |
32 | $form->Buttons(array('btnSave' => false, 'btnApply' => true));
33 | $form->Show();
34 |
--------------------------------------------------------------------------------
/install/tools/migrate:
--------------------------------------------------------------------------------
1 | getLocalization('uninstall');
4 | $options = \WS\ReduceMigrations\Module::getInstance()->getOptions();
5 | $form = new CAdminForm('ew', array(
6 | array(
7 | 'DIV' => 't1',
8 | 'TAB' => $localization->getDataByPath('tab'),
9 | )
10 | ));
11 | ShowMessage(array(
12 | 'MESSAGE' => $localization->getDataByPath('description'),
13 | 'TYPE' => 'OK'
14 | ));
15 |
16 | $errors && ShowError(implode(', ', $errors));
17 | $form->Begin(array(
18 | 'FORM_ACTION' => $APPLICATION->GetCurUri()
19 | ));
20 | $form->BeginNextFormTab();
21 | $form->AddCheckBoxField('data[removeAll]', $localization->getDataByPath('fields.removeAll'), true, "Y", false);
22 | $form->BeginCustomField('data[remove]', '');
23 | ?>
24 |
25 |
26 |
27 |
28 | EndCustomField('data[remove]');
30 | $form->Buttons(array('btnSave' => false, 'btnApply' => true));
31 | $form->Show();
--------------------------------------------------------------------------------
/install/version.php:
--------------------------------------------------------------------------------
1 | "1.1.0",
4 | "VERSION_DATE" => "2019-09-07 15:12:00"
5 | );
6 |
--------------------------------------------------------------------------------
/lang/en/admin.php:
--------------------------------------------------------------------------------
1 | array(
4 | 'title' => 'Migrations management',
5 | 'list' => array(
6 | 'scenarios' => 'New scenarios'
7 | ),
8 | 'priority' => array(
9 | 'priority' => 'Migration priority:',
10 | 'high' => 'High:',
11 | 'medium' => 'Medium:',
12 | 'optional' => 'Optional:',
13 | ),
14 | 'skipOptional' => 'Skip migrations with type "Optional"',
15 | 'errorList' => 'Unsuccessful applied migrations',
16 | 'appliedList' => 'Successful applied migrations',
17 | 'approximatelyTime' => 'Approximately time of migrations',
18 | 'timeLang' => [
19 | 'minutes' => 'min',
20 | 'seconds' => 'sec'
21 | ],
22 | 'btnRollback' => 'Undo last change',
23 | 'btnApply' => 'Apply',
24 | 'lastSetup' => array(
25 | 'sectionName' => 'Last update :time: - :user:'
26 | ),
27 | 'common' => array(
28 | 'listEmpty' => 'List is empty',
29 | 'reference-fix' => 'References synchronizing',
30 | 'pageEmpty' => 'Data for update don`t exists yet'
31 | ),
32 | 'newChangesDetail' => 'Changes list',
33 | 'newChangesTitle' => 'New changes',
34 | 'errorWindow' => 'Error info',
35 | 'diagnostic' => 'Errors diagnosis, the use of the migration is possible only after the correction',
36 | 'platformVersion' => array(
37 | 'ok' => 'Platform version',
38 | 'error' => 'Incorrect platform version',
39 | 'setup' => 'Setup',
40 | )
41 | ),
42 | 'applyError' => array(
43 | 'message' => 'Message',
44 | 'data' => 'Data',
45 | 'trace' => 'Call stack',
46 | 'error' => array(
47 | 'modelNotExists' => 'Data for record id =: id: does not exist'
48 | )
49 | ),
50 | 'createScenario' => array(
51 | 'title' => 'The script scenario',
52 | 'field' => array(
53 | 'name' => 'Title',
54 | 'priority' => 'Priority',
55 | 'time' => 'Approximately migration time(seconds)',
56 | ),
57 | 'priority' => array(
58 | 'high' => 'High',
59 | 'medium' => 'Medium',
60 | 'optional' => 'Optional',
61 | ),
62 | 'path-to-file' => 'Class file migration is #path#',
63 | 'save-file-error' => 'An error occured save file',
64 | 'button' => array(
65 | 'create' => 'Create migration scenario'
66 | )
67 | ),
68 | 'log' => array(
69 | 'title' => 'Updates log',
70 | 'fields' => array(
71 | 'updateDate' => 'Date',
72 | 'description' => 'Update features',
73 | 'hash' => 'Migration hash',
74 | 'dispatcher' => 'Update by'
75 | ),
76 | 'messages' => array(
77 | 'InsertReference' => 'Insert other reference',
78 | 'view' => 'Analysis of changes',
79 | 'pages' => 'Pages',
80 | 'actualization' => 'Actualization sources',
81 | 'descriptionMoreLink' => 'detail',
82 | 'errorWindow' => 'Error information'
83 | )
84 | ),
85 | 'detail' => array(
86 | 'title' => '#date. #source. Update by - #deployer',
87 | 'tabs' => array(
88 | 'diff' => 'Features',
89 | 'final' => 'Update result',
90 | 'merge' => 'Data before update'
91 | ),
92 | 'message' => array(
93 | 'nobody' => 'The site is not updated',
94 | 'show' => 'show data',
95 | 'hide' => 'hide data',
96 | ),
97 | 'serviceLabels' => array(
98 | '~reference' => 'HASH',
99 | '~property_list_values' => 'VALUES',
100 | 'Reference fix' => 'Register links with the essence of the platform',
101 | 'Insert reference' => 'The new entity reference',
102 | 'reference' => 'HASH',
103 | 'group' => 'Group entity ( the handler )',
104 | 'dbVersion' => 'Platform version'
105 | )
106 | ),
107 | 'cli' => array(
108 | 'common' => array(
109 | 'reference-fix' => 'References synchronizing'
110 | ),
111 | ),
112 | );
--------------------------------------------------------------------------------
/lang/en/info.php:
--------------------------------------------------------------------------------
1 | 'Data migrations',
4 | 'description' => 'Allows you to synchronize data from different copies of site',
5 | 'partner' => array(
6 | 'name' => 'WorkSolutions',
7 | 'url' => 'http://www.worksolutions.ru'
8 | )
9 | );
--------------------------------------------------------------------------------
/lang/en/menu.php:
--------------------------------------------------------------------------------
1 | 'Reduce Migrations',
5 | 'apply' => 'Update',
6 | 'log' => 'Log',
7 | 'createScenario' => 'Scenario',
8 | 'diagnostic' => 'Diagnostic'
9 | );
--------------------------------------------------------------------------------
/lang/en/setup.php:
--------------------------------------------------------------------------------
1 | 'Installing the Data migrations module',
4 | 'name' => 'WS ReduceMigrations',
5 | 'tab' => 'Settings',
6 | 'description' => 'You must specify the directory location of the migration file. relative to the root directory of the project. ($ SERVER [\'DOCUMENT_ROOT\']) ',
7 | 'fields' => array(
8 | 'catalog' => 'Directory path',
9 | 'apply' => 'Set Up'
10 | ),
11 | 'errors' => array(
12 | 'notCreateDir' => 'Unable to create directory',
13 | 'catalogFieldEmpty' => 'Catalog field is empty'
14 | ),
15 | 'section' => array(
16 | 'disableHandlers' => 'Use synchronization'
17 | )
18 | );
--------------------------------------------------------------------------------
/lang/en/tests.php:
--------------------------------------------------------------------------------
1 | array(
4 | 'name' => 'WorkSolutions. Reduce Migrations module',
5 | 'report' => array(
6 | 'completed' => 'Completed',
7 | 'assertions' => 'Assertions'
8 | )
9 | ),
10 | 'cases' => array(
11 | \WS\ReduceMigrations\Tests\Cases\IblockBuilderCase::className() => array(
12 | 'name' => 'IblockBuilder test',
13 | 'description' => '',
14 | 'errors' => array(
15 | )
16 | ),
17 | \WS\ReduceMigrations\Tests\Cases\HighLoadBlockBuilderCase::className() => array(
18 | 'name' => 'HighLoadBlockBuilder',
19 | 'description' => '',
20 | 'errors' => array(
21 | )
22 | ),
23 | \WS\ReduceMigrations\Tests\Cases\AgentBuilderCase::className() => array(
24 | 'name' => 'AgentBuilder',
25 | 'description' => '',
26 | 'errors' => array(
27 | )
28 | ),
29 | \WS\ReduceMigrations\Tests\Cases\EventsBuilderCase::className() => array(
30 | 'name' => 'EventsBuilder',
31 | 'description' => '',
32 | 'errors' => array(
33 | )
34 | ),
35 | \WS\ReduceMigrations\Tests\Cases\FormBuilderCase::className() => array(
36 | 'name' => 'FormBuilder',
37 | 'description' => '',
38 | 'errors' => array(
39 | )
40 | ),
41 | \WS\ReduceMigrations\Tests\Cases\TableBuilderCase::className() => array(
42 | 'name' => 'TableBuilder',
43 | 'description' => '',
44 | 'errors' => array(
45 | )
46 | ),
47 | )
48 | );
--------------------------------------------------------------------------------
/lang/en/uninstall.php:
--------------------------------------------------------------------------------
1 | 'Deleting data of Reduce Migrations module',
4 | 'tab' => 'Delete options',
5 | 'description' => "When you delete data , the project will not be able to work with the current migrations.\n"
6 | ."You will need to initialize the mechanism of migration once again , starting with the current version .\n"
7 | ."Information to remove : tables migration , customization , migration catalog (if used versioning system"
8 | ."should register changes )",
9 | 'fields' => array(
10 | 'removeAll' => 'Erase migration data'
11 | )
12 | );
--------------------------------------------------------------------------------
/lang/ru/admin.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/admin.php
--------------------------------------------------------------------------------
/lang/ru/info.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/info.php
--------------------------------------------------------------------------------
/lang/ru/menu.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/menu.php
--------------------------------------------------------------------------------
/lang/ru/setup.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/setup.php
--------------------------------------------------------------------------------
/lang/ru/tests.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/tests.php
--------------------------------------------------------------------------------
/lang/ru/uninstall.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lang/ru/uninstall.php
--------------------------------------------------------------------------------
/lib/applyresult.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations;
7 |
8 |
9 | class ApplyResult {
10 | private $success;
11 | private $message;
12 | private $id;
13 |
14 | /**
15 | * @param string $value
16 | * @return $this
17 | */
18 | public function setMessage($value) {
19 | $this->message = $value;
20 | return $this;
21 | }
22 |
23 | /**
24 | * @return string
25 | */
26 | public function getMessage() {
27 | return $this->message;
28 | }
29 |
30 | /**
31 | * @return boolean
32 | */
33 | public function isSuccess() {
34 | return $this->success;
35 | }
36 |
37 | /**
38 | * @param boolean $value
39 | * @return $this
40 | */
41 | public function setSuccess($value) {
42 | $this->success = $value;
43 | return $this;
44 | }
45 |
46 | /**
47 | * @return mixed
48 | */
49 | public function getId() {
50 | return $this->id;
51 | }
52 |
53 | /**
54 | * @param mixed $id
55 | * @return $this
56 | */
57 | public function setId($id) {
58 | $this->id = $id;
59 | return $this;
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/lib/builder/agentbuilder.php:
--------------------------------------------------------------------------------
1 | commit($agent);
23 | return $agent;
24 | }
25 |
26 | /**
27 | * @param string $agentFunction
28 | * @param \Closure $callback
29 | * @return Agent
30 | * @throws BuilderException
31 | */
32 | public function updateAgent($agentFunction, $callback) {
33 | $data = $this->findAgent($agentFunction);
34 | $agent = new Agent($agentFunction);
35 | $agent->setId($data['ID']);
36 | $agent->markClean();
37 | $callback($agent);
38 | $this->commit($agent);
39 | return $agent;
40 | }
41 |
42 | /**
43 | * @var Agent $agent
44 | * @throws BuilderException
45 | */
46 | private function commit($agent) {
47 | global $DB, $APPLICATION;
48 | $DB->StartTransaction();
49 | $gw = new \CAgent();
50 | try {
51 | if ($agent->getId() > 0) {
52 | if ($agent->isDirty()) {
53 | $res = $gw->Update($agent->getId(), $agent->getData());
54 | if (!$res) {
55 | throw new BuilderException("Agent wasn't updated");
56 | }
57 | }
58 | } else {
59 | $res = $gw->AddAgent(
60 | $agent->getAttribute('NAME'),
61 | $agent->getAttribute('MODULE_ID'),
62 | $agent->getAttribute('IS_PERIOD'),
63 | $agent->getAttribute('AGENT_INTERVAL'),
64 | '',//bitrix doesn't use this parameter
65 | $agent->getAttribute('ACTIVE'),
66 | $agent->getAttribute('NEXT_EXEC'),
67 | $agent->getAttribute('SORT'),
68 | $agent->getAttribute('USER_ID')
69 | );
70 | if (!$res) {
71 | throw new BuilderException("Agent wasn't created: " . $APPLICATION->GetException()->GetString());
72 | }
73 | $agent->setId($res);
74 | }
75 |
76 | } catch (BuilderException $e) {
77 | $DB->Rollback();
78 | throw new BuilderException($e->getMessage());
79 | }
80 | $DB->Commit();
81 | }
82 |
83 | /**
84 | * @param $callback
85 | * @return array
86 | * @throws BuilderException
87 | */
88 | private function findAgent($callback) {
89 | $agent = \CAgent::GetList(null, array(
90 | 'NAME' => $callback,
91 | ))->Fetch();
92 | if (empty($agent)) {
93 | throw new BuilderException("Agent {$callback} not found");
94 | }
95 | return $agent;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/lib/builder/builderexception.php:
--------------------------------------------------------------------------------
1 | callback($callback);
26 | }
27 |
28 | public function getMap() {
29 | return array(
30 | 'sort' => 'SORT',
31 | 'active' => 'ACTIVE',
32 | 'module' => 'MODULE_ID',
33 | 'callback' => 'NAME',
34 | 'userId' => 'USER_ID',
35 | 'isPeriod' => 'IS_PERIOD',
36 | 'interval' => 'AGENT_INTERVAL',
37 | 'nextExec' => 'NEXT_EXEC',
38 | );
39 | }
40 |
41 | /**
42 | * @param int $id
43 | * @return Agent
44 | */
45 | public function setId($id) {
46 | $this->id = $id;
47 | return $this;
48 | }
49 |
50 | /**
51 | * @return int
52 | */
53 | public function getId() {
54 | return $this->id;
55 | }
56 |
57 | public static function create($callback) {
58 | $agent = new Agent($callback);
59 | $agent
60 | ->sort(self::DEFAULT_SORT)
61 | ->active(true)
62 | ->interval(self::DEFAULT_INTERVAL)
63 | ->isPeriod(false)
64 | ->nextExec(new DateTime());
65 |
66 | return $agent;
67 | }
68 |
69 | /**
70 | * @param bool $active
71 | * @return Agent
72 | */
73 | public function active($active) {
74 | $this->setAttribute('ACTIVE', $active ? 'Y' : 'N');
75 | return $this;
76 | }
77 |
78 | /**
79 | * @param bool $isPeriod
80 | * @return Agent
81 | */
82 | public function isPeriod($isPeriod) {
83 | $this->setAttribute('IS_PERIOD', $isPeriod ? 'Y' : 'N');
84 | return $this;
85 | }
86 |
87 | }
--------------------------------------------------------------------------------
/lib/builder/entity/base.php:
--------------------------------------------------------------------------------
1 | params[$attributeName] = $value;
19 | $this->markDirty();
20 | return $this;
21 | }
22 |
23 | /**
24 | * @param string $name
25 | *
26 | * @return mixed
27 | */
28 | public function getAttribute($name) {
29 | return $this->params[$name];
30 | }
31 | /**
32 | * @return array
33 | */
34 | public function getData() {
35 | return $this->params;
36 | }
37 |
38 | protected abstract function getMap();
39 |
40 | public function __call($name, $arguments) {
41 | $map = $this->getMap();
42 | if (!isset($map[$name])) {
43 | throw new \Exception("Call to undefined method {$name}");
44 | }
45 | $this->setAttribute($map[$name], $arguments[0]);
46 | return $this;
47 | }
48 |
49 | public function isDirty() {
50 | return $this->isDirty;
51 | }
52 |
53 | public function markDirty() {
54 | $this->isDirty = true;
55 | }
56 |
57 | public function markClean() {
58 | $this->isDirty = false;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/builder/entity/enumvariant.php:
--------------------------------------------------------------------------------
1 | $item) {
19 | $this->setAttribute($key, $item);
20 | }
21 | if ($data['ID']) {
22 | $this->setId($data['ID']);
23 | }
24 | $this->value($value);
25 | }
26 |
27 | /**
28 | * @param int $id
29 | * @return EnumVariant
30 | */
31 | public function setId($id) {
32 | $this->id = $id;
33 | return $this;
34 | }
35 |
36 | /**
37 | * @return int
38 | */
39 | public function getId() {
40 | return $this->id;
41 | }
42 |
43 | protected function getMap() {
44 | return array(
45 | 'value' => 'VALUE',
46 | 'sort' => 'SORT',
47 | );
48 | }
49 |
50 | /**
51 | * @param string $value
52 | *
53 | * @return EnumVariant
54 | */
55 | public function xmlId($value) {
56 | $this->setAttribute('XML_ID', $value);
57 | $this->setAttribute('EXTERNAL_ID', $value);
58 | return $this;
59 | }
60 |
61 |
62 | /**
63 | * @param bool $value
64 | *
65 | * @return EnumVariant
66 | */
67 | public function byDefault($value = true) {
68 | $this->setAttribute('DEF', $value ? 'Y' : 'N');
69 | return $this;
70 | }
71 |
72 | /**
73 | * @return EnumVariant
74 | */
75 | public function markDeleted() {
76 | $this->setAttribute('DEL', 'Y');
77 | return $this;
78 | }
79 |
80 | public function needToDelete() {
81 | return $this->getAttribute('DEL');
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/lib/builder/entity/eventmessage.php:
--------------------------------------------------------------------------------
1 | $value) {
27 | $this->setAttribute($code, $value);
28 | }
29 | $this
30 | ->emailFrom($from)
31 | ->active()
32 | ->emailTo($to)
33 | ->siteId($siteId)
34 | ->dateUpdate(new DateTime());
35 |
36 | }
37 |
38 | /**
39 | * @param int $id
40 | * @return EventMessage
41 | */
42 | public function setId($id) {
43 | $this->id = $id;
44 | return $this;
45 | }
46 |
47 | /**
48 | * @return int
49 | */
50 | public function getId() {
51 | return $this->id;
52 | }
53 |
54 | protected function getMap() {
55 | return array(
56 | 'id' => 'ID',
57 | 'siteId' => 'LID',
58 | 'active' => 'ACTIVE',
59 | 'emailFrom' => 'EMAIL_FROM',
60 | 'emailTo' => 'EMAIL_TO',
61 | 'subject' => 'SUBJECT',
62 | 'body' => 'MESSAGE',
63 | 'bodyType' => 'BODY_TYPE',
64 | 'bcc' => 'BCC',
65 | 'dateUpdate' => 'TIMESTAMP_X',
66 | );
67 | }
68 |
69 | /**
70 | * @param bool $active
71 | * @return EventMessage
72 | */
73 | public function active($active = true) {
74 | $this->setAttribute('ACTIVE', $active ? 'Y' : 'N');
75 | return $this;
76 | }
77 |
78 | public function remove() {
79 | $this->forRemove = true;
80 | }
81 |
82 | public function isRemoved() {
83 | return $this->forRemove;
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/lib/builder/entity/eventtype.php:
--------------------------------------------------------------------------------
1 | eventName($eventName)
25 | ->lid($lid);
26 | }
27 |
28 | protected function getMap() {
29 | return array(
30 | 'sort' => 'SORT',
31 | 'eventName' => 'EVENT_NAME',
32 | 'lid' => 'LID',
33 | 'name' => 'NAME',
34 | 'description' => 'DESCRIPTION',
35 | );
36 | }
37 |
38 | /**
39 | * @param int $id
40 | * @return EventType
41 | */
42 | public function setId($id) {
43 | $this->id = $id;
44 | return $this;
45 | }
46 |
47 | /**
48 | * @return int
49 | */
50 | public function getId() {
51 | return $this->id;
52 | }
53 |
54 | /**
55 | * @return string
56 | */
57 | public function getEventName() {
58 | return $this->getAttribute('EVENT_NAME');
59 | }
60 |
61 | /**
62 | * @param string $from
63 | * @param string $to
64 | * @param string $siteId
65 | *
66 | * @return EventMessage
67 | */
68 | public function addEventMessage($from, $to, $siteId) {
69 | $eventMessage = new EventMessage($from, $to, $siteId);
70 | $this->eventMessages[] = $eventMessage;
71 | return $eventMessage;
72 | }
73 |
74 | /**
75 | *
76 | * @return EventMessage[]
77 | */
78 | public function loadEventMessages() {
79 | $messages = $this->findMessages();
80 | foreach ($messages as $message) {
81 | $eventMessage = new EventMessage($message['EMAIL_FROM'], $message['EMAIL_TO'], $message['LID'], $message);
82 | $eventMessage->setId($message['ID']);
83 | $eventMessage->markClean();
84 | $this->eventMessages[] = $eventMessage;
85 | }
86 | return $this->eventMessages;
87 | }
88 |
89 | /**
90 | * @return mixed
91 | */
92 | public function getEventMessages() {
93 | return $this->eventMessages;
94 | }
95 |
96 | /**
97 | * @return array
98 | */
99 | private function findMessages() {
100 | $res = EventMessageTable::getList(array(
101 | 'filter' => array(
102 | 'EVENT_NAME' => $this->getEventName()
103 | )
104 | ));
105 |
106 | return $res->fetchAll();
107 | }
108 | }
--------------------------------------------------------------------------------
/lib/builder/entity/fieldwrapper.php:
--------------------------------------------------------------------------------
1 | field = $field;
15 | }
16 |
17 | /**
18 | * @return string
19 | */
20 | public function getName() {
21 | return strtoupper($this->field->getColumnName());
22 | }
23 |
24 | /**
25 | * @param bool $increment
26 | *
27 | * @return $this
28 | */
29 | public function autoincrement($increment = true) {
30 | $this->field->configureAutocomplete($increment);
31 |
32 | return $this;
33 | }
34 |
35 | /**
36 | * @param bool $primary
37 | *
38 | * @return $this
39 | */
40 | public function primary($primary = true) {
41 | $this->field->configurePrimary($primary);
42 |
43 | return $this;
44 | }
45 |
46 | /**
47 | * @param bool $unique
48 | *
49 | * @return $this
50 | */
51 | public function unique($unique = true) {
52 | $this->field->configureUnique($unique);
53 |
54 | return $this;
55 | }
56 |
57 | /**
58 | * @param bool $required
59 | *
60 | * @return $this
61 | */
62 | public function required($required = true) {
63 | $this->field->configureRequired($required);
64 |
65 | return $this;
66 | }
67 |
68 | /**
69 | * @return ScalarField
70 | */
71 | public function getField() {
72 | return $this->field;
73 | }
74 |
75 | /**
76 | * @return bool
77 | */
78 | public function isAutoincrement() {
79 | return $this->field->isAutocomplete();
80 | }
81 |
82 | /**
83 | * @return bool
84 | */
85 | public function isPrimary() {
86 | return $this->field->isPrimary();
87 | }
88 |
89 | /**
90 | * @return bool
91 | */
92 | public function isUnique() {
93 | return $this->field->isUnique();
94 | }
95 |
96 | /**
97 | * @return bool
98 | */
99 | public function isRequired() {
100 | return $this->field->isRequired();
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/lib/builder/entity/form.php:
--------------------------------------------------------------------------------
1 | name($name)
38 | ->sid($sid);
39 | }
40 |
41 | public function getMap() {
42 | return array(
43 | 'name' => 'NAME',
44 | 'sid' => 'SID',
45 | 'sort' => 'C_SORT',
46 | 'button' => 'BUTTON',
47 | 'useRestrictions' => 'USE_RESTRICTIONS',
48 | 'useCaptcha' => 'USE_CAPTCHA',
49 | 'restrictUser' => 'RESTRICT_USER',
50 | 'restrictTime' => 'RESTRICT_TIME',
51 | 'description' => 'DESCRIPTION',
52 | 'descriptionType' => 'DESCRIPTION_TYPE',
53 | 'filterResultTemplate' => 'FILTER_RESULT_TEMPLATE',
54 | 'tableResultTemplate' => 'TABLE_RESULT_TEMPLATE',
55 | 'statEvent1' => 'STAT_EVENT1',
56 | 'statEvent2' => 'STAT_EVENT2',
57 | 'statEvent3' => 'STAT_EVENT3',
58 | 'image' => 'arIMAGE',
59 | 'arSiteId' => 'arSITE',
60 | 'arMailTemplate' => 'arMAIL_TEMPLATE',
61 | 'arMenu' => 'arMENU',
62 | 'arGroup' => 'arGROUP',
63 | );
64 | }
65 |
66 | /**
67 | * @param int $id
68 | * @return Form
69 | */
70 | public function setId($id) {
71 | $this->id = $id;
72 | return $this;
73 | }
74 |
75 | /**
76 | * @return int
77 | */
78 | public function getId() {
79 | return $this->id;
80 | }
81 |
82 | /**
83 | * @param bool $useCaptcha
84 | * @return Form
85 | */
86 | public function useCaptcha($useCaptcha) {
87 | $this->setAttribute('USE_CAPTCHA', $useCaptcha ? 'Y' : 'N');
88 | return $this;
89 | }
90 |
91 | /**
92 | * @param $title
93 | * @return FormStatus
94 | * @throws BuilderException
95 | */
96 | public function addStatus($title) {
97 |
98 | $status = new FormStatus($title);
99 | $this->statuses[] = $status;
100 | return $status;
101 | }
102 |
103 | /**
104 | * @param $title
105 | * @return FormStatus
106 | * @throws BuilderException
107 | */
108 | public function updateStatus($title) {
109 |
110 | $data = $this->findStatus($title);
111 | $status = new FormStatus($title);
112 | $status->setId($data['ID']);
113 | $status->markClean();
114 | $this->statuses[] = $status;
115 | return $status;
116 | }
117 |
118 | /**
119 | * @param $title
120 | * @return array
121 | * @throws BuilderException
122 | */
123 | private function findStatus($title) {
124 | $status = \CFormStatus::GetList($this->getId(), $by, $order, array(
125 | 'TITLE' => $title,
126 | ), $isFiltered)->Fetch();
127 |
128 | if (empty($status)) {
129 | throw new BuilderException("Form status '{$title}' not found");
130 | }
131 | return $status;
132 | }
133 |
134 | /**
135 | * @param $sid
136 | * @return FormField
137 | * @throws BuilderException
138 | */
139 | public function addField($sid) {
140 | $field = new FormField($sid);
141 | $this->fields[] = $field;
142 | return $field;
143 | }
144 |
145 | /**
146 | * @param $sid
147 | * @return FormField
148 | * @throws BuilderException
149 | */
150 | public function updateField($sid) {
151 | $data = $this->findField($sid);
152 | $field = new FormField($sid);
153 | $field->setId($data['ID']);
154 | $field->markClean();
155 | $this->fields[] = $field;
156 | return $field;
157 | }
158 |
159 | /**
160 | * @param $sid
161 | * @return array
162 | * @throws BuilderException
163 | */
164 | private function findField($sid) {
165 | $field = \CFormField::GetList($this->getId(), 'ALL', $by, $order, array(
166 | 'SID' => $sid,
167 | ), $isFiltered)->Fetch();
168 | if (empty($field)) {
169 | throw new BuilderException("Form field '{$sid}' not found");
170 | }
171 | return $field;
172 | }
173 |
174 | /**
175 | * @return FormStatus[]
176 | */
177 | public function getStatuses() {
178 | return $this->statuses;
179 | }
180 |
181 | /**
182 | * @return FormField[]
183 | */
184 | public function getFields() {
185 | return $this->fields;
186 | }
187 |
188 | }
--------------------------------------------------------------------------------
/lib/builder/entity/formanswer.php:
--------------------------------------------------------------------------------
1 | message($message);
32 | }
33 |
34 | public function getMap() {
35 | return array(
36 | 'sort' => 'C_SORT',
37 | 'message' => 'MESSAGE',
38 | 'value' => 'VALUE',
39 | 'active' => 'ACTIVE',
40 | 'fieldType' => 'FIELD_TYPE',
41 | 'fieldWidth' => 'FIELD_WIDTH',
42 | 'fieldHeight' => 'FIELD_HEIGHT',
43 | 'fieldParam' => 'FIELD_PARAM',
44 | 'del' => 'DEL',
45 | );
46 | }
47 |
48 | /**
49 | * @param int $id
50 | * @return FormAnswer
51 | */
52 | public function setId($id) {
53 | $this->id = $id;
54 | return $this;
55 | }
56 |
57 | /**
58 | * @return int
59 | */
60 | public function getId() {
61 | return $this->id;
62 | }
63 |
64 | /**
65 | * @param bool $active
66 | * @return FormAnswer
67 | */
68 | public function active($active) {
69 | $this->setAttribute('ACTIVE', $active ? 'Y' : 'N');
70 | return $this;
71 | }
72 |
73 | public function needDelete() {
74 | return $this->getAttribute('DEL') == 'Y';
75 | }
76 |
77 | public function markDelete() {
78 | $this->setAttribute('DEL', 'Y');
79 | }
80 |
81 |
82 | }
--------------------------------------------------------------------------------
/lib/builder/entity/formfield.php:
--------------------------------------------------------------------------------
1 | setAttribute('SID', $sid);
38 | $this->answers = array();
39 | }
40 |
41 | public function getMap() {
42 | return array(
43 | 'sort' => 'C_SORT',
44 | 'sid' => 'SID',
45 | 'formId' => 'FORM_ID',
46 | 'active' => 'ACTIVE',
47 | 'additional' => 'ADDITIONAL',
48 | 'fieldType' => 'FIELD_TYPE',
49 | 'title' => 'TITLE',
50 | 'titleType' => 'TITLE_TYPE',
51 | 'required' => 'REQUIRED',
52 | 'filterTitle' => 'FILTER_TITLE',
53 | 'inResultsTable' => 'IN_RESULTS_TABLE',
54 | 'inExcelTable' => 'IN_EXCEL_TABLE',
55 | 'resultsTableTitle' => 'RESULTS_TABLE_TITLE',
56 | 'comments' => 'COMMENTS',
57 | 'arImage' => 'arIMAGE',
58 | 'arFilterUser' => 'arFILTER_USER',
59 | 'arFilterAnswerText' => 'arFILTER_ANSWER_TEXT',
60 | 'arFilterAnswerValue' => 'arFILTER_ANSWER_VALUE',
61 | 'arFilterField' => 'arFILTER_FIELD',
62 | );
63 | }
64 |
65 | /**
66 | * @param int $id
67 | * @return FormField
68 | */
69 | public function setId($id) {
70 | $this->id = $id;
71 | return $this;
72 | }
73 |
74 | /**
75 | * @return int
76 | */
77 | public function getId() {
78 | return $this->id;
79 | }
80 |
81 | /**
82 | * @param bool $active
83 | * @return FormField
84 | */
85 | public function active($active) {
86 | $this->setAttribute('ACTIVE', $active ? 'Y' : 'N');
87 | return $this;
88 | }
89 |
90 | /**
91 | * @return FormField
92 | */
93 | public function asQuestion() {
94 | $this->setAttribute('ADDITIONAL', 'N');
95 | return $this;
96 | }
97 |
98 | /**
99 | * @return FormField
100 | */
101 | public function asField() {
102 | $this->setAttribute('ADDITIONAL', 'Y');
103 | return $this;
104 | }
105 |
106 | /**
107 | * @param bool $required
108 | * @return FormField
109 | */
110 | public function required($required = true) {
111 | $this->setAttribute('REQUIRED', $required ? "Y" : "N");
112 | return $this;
113 | }
114 |
115 | /**
116 | * @param bool $inResultsTable
117 | * @return FormField
118 | */
119 | public function inResultsTable($inResultsTable) {
120 | $this->setAttribute('IN_RESULTS_TABLE', $inResultsTable ? "Y" : "N");
121 | return $this;
122 | }
123 |
124 | /**
125 | * @param bool $inExcelTable
126 | * @return FormField
127 | */
128 | public function inExcelTable($inExcelTable) {
129 | $this->setAttribute('IN_EXCEL_TABLE', $inExcelTable ? "Y" : "N");
130 | return $this;
131 | }
132 |
133 | /**
134 | * @param $message
135 | * @return FormAnswer
136 | */
137 | public function addAnswer($message) {
138 | $answer = new FormAnswer($message);
139 | $this->answers[] = $answer;
140 | return $answer;
141 | }
142 |
143 | /**
144 | * @param $message
145 | * @return FormAnswer
146 | * @throws BuilderException
147 | */
148 | public function updateAnswer($message) {
149 | $data = $this->findAnswer($message);
150 | $answer = new FormAnswer($message);
151 | $answer->setId($data['ID']);
152 | $answer->markClean();
153 | $this->answers[] = $answer;
154 | return $answer;
155 | }
156 |
157 | /**
158 | * @param $message
159 | *
160 | * @return FormAnswer
161 | * @throws BuilderException
162 | */
163 | public function removeAnswer($message) {
164 | $data = $this->findAnswer($message);
165 | $answer = new FormAnswer($message);
166 | $answer->markDelete();
167 | $answer->setId($data['ID']);
168 | $this->answers[] = $answer;
169 | return $answer;
170 | }
171 |
172 | private function findAnswer($message) {
173 | $data = \CFormAnswer::GetList($this->getId(), $by = null, $order = null, array(
174 | 'MESSAGE' => $message
175 | ), $isFiltered = false)->Fetch();
176 |
177 | if (empty($data)) {
178 | throw new BuilderException("Answer '{$message}' not found");
179 | }
180 | return $data;
181 | }
182 |
183 | /**
184 | * @return FormAnswer[]
185 | */
186 | public function getAnswers() {
187 | return $this->answers;
188 | }
189 |
190 | }
--------------------------------------------------------------------------------
/lib/builder/entity/formstatus.php:
--------------------------------------------------------------------------------
1 | title($title)
28 | ->dateUpdate(new DateTime());
29 | }
30 |
31 | public function getMap() {
32 | return array(
33 | 'sort' => 'C_SORT',
34 | 'dateUpdate' => 'TIMESTAMP_X',
35 | 'active' => 'ACTIVE',
36 | 'title' => 'TITLE',
37 | 'description' => 'DESCRIPTION',
38 | 'isDefault' => 'DEFAULT_VALUE',
39 | 'css' => 'CSS',
40 | 'handlerOut' => 'HANDLER_OUT',
41 | 'handlerIn' => 'HANDLER_IN',
42 | 'arGroupCanView' => 'arPERMISSION_VIEW',
43 | 'arGroupCanMove' => 'arPERMISSION_MOVE',
44 | 'arGroupCanEdit' => 'arPERMISSION_EDIT',
45 | 'arGroupCanDelete' => 'arPERMISSION_DELETE',
46 | );
47 | }
48 |
49 | /**
50 | * @param int $id
51 | * @return FormStatus
52 | */
53 | public function setId($id) {
54 | $this->id = $id;
55 | return $this;
56 | }
57 |
58 | /**
59 | * @return int
60 | */
61 | public function getId() {
62 | return $this->id;
63 | }
64 |
65 | /**
66 | * @param bool $active
67 | * @return FormStatus
68 | */
69 | public function active($active) {
70 | $this->setAttribute('ACTIVE', $active ? 'Y' : 'N');
71 | return $this;
72 | }
73 |
74 | /**
75 | * @param bool $isDefault
76 | * @return FormStatus
77 | */
78 | public function byDefault($isDefault = true) {
79 | $this->setAttribute('DEFAULT_VALUE', $isDefault ? 'Y' : 'N');
80 | return $this;
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/lib/builder/entity/highloadblock.php:
--------------------------------------------------------------------------------
1 | id = $id;
22 | $this->name($name);
23 | $this->tableName($tableName);
24 | }
25 |
26 | public function getMap() {
27 | return array(
28 | 'name' => 'NAME',
29 | 'tableName' => 'TABLE_NAME',
30 | );
31 | }
32 |
33 | /**
34 | * @param int $id
35 | * @return HighLoadBlock
36 | */
37 | public function setId($id) {
38 | $this->id = $id;
39 | return $this;
40 | }
41 |
42 | /**
43 | * @return int
44 | */
45 | public function getId() {
46 | return $this->id;
47 | }
48 |
49 | /**
50 | * @param $code
51 | * @return UserField
52 | */
53 | public function addField($code) {
54 | return $this->addUserField($code);
55 | }
56 |
57 | /**
58 | * @param $code
59 | * @return UserField
60 | * @throws BuilderException
61 | */
62 | public function updateField($code) {
63 | if (!$this->getId()) {
64 | throw new BuilderException('Set higloadBlock for continue');
65 | }
66 |
67 | return $this->updateUserField($code, "HLBLOCK_{$this->getId()}");
68 | }
69 |
70 | /**
71 | * @return UserField[]
72 | */
73 | public function getFields() {
74 | return $this->getUserFields();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/builder/entity/iblocktype.php:
--------------------------------------------------------------------------------
1 | ['NAME'=>'Catalog', 'SECTION_NAME'=>'Sections', 'ELEMENT_NAME'=>'Products']]
12 | * @package WS\ReduceMigrations\Builder\Entity
13 | */
14 | class IblockType extends Base {
15 |
16 | public function __construct($type) {
17 | $this->setId($type);
18 | $this->type($type);
19 | }
20 |
21 | /**
22 | * @param string $id
23 | * @return IblockType
24 | */
25 | public function setId($id) {
26 | $this->setAttribute('ID', $id);
27 | return $this;
28 | }
29 |
30 | /**
31 | * @return string
32 | */
33 | public function getId() {
34 | return $this->getAttribute('ID');
35 | }
36 |
37 | protected function getMap() {
38 | return array(
39 | 'type' => 'IBLOCK_TYPE_ID',
40 | 'sort' => 'SORT',
41 | 'sections' => 'SECTIONS',
42 | 'lang' => 'LANG',
43 | );
44 | }
45 |
46 | /**
47 | * @param bool $inRss
48 | * @return IblockType
49 | */
50 | public function inRss($inRss) {
51 | $this->setAttribute('IN_RSS', $inRss ? 'Y' : 'N');
52 | return $this;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/builder/entity/table.php:
--------------------------------------------------------------------------------
1 | name = $name;
20 | }
21 |
22 | private function addField($scalarField) {
23 | $field = new FieldWrapper($scalarField);
24 | $this->fields[$field->getName()] = $field;
25 | return $field;
26 | }
27 |
28 | /**
29 | * @param $name
30 | *
31 | * @return FieldWrapper
32 | */
33 | public function string($name) {
34 | return $this->addField(new StringField($name));
35 | }
36 |
37 | /**
38 | * @param $name
39 | *
40 | * @return FieldWrapper
41 | */
42 | public function integer($name) {
43 | return $this->addField(new IntegerField($name));
44 | }
45 |
46 | /**
47 | * @param $name
48 | *
49 | * @return FieldWrapper
50 | */
51 | public function float($name) {
52 | return $this->addField(new FloatField($name));
53 | }
54 |
55 | /**
56 | * @param $name
57 | *
58 | * @return FieldWrapper
59 | */
60 | public function datetime($name) {
61 | return $this->addField(new DatetimeField($name));
62 | }
63 |
64 | /**
65 | * @param $name
66 | *
67 | * @return FieldWrapper
68 | */
69 | public function date($name) {
70 | return $this->addField(new DateField($name));
71 | }
72 |
73 | /**
74 | * @param $name
75 | *
76 | * @return FieldWrapper
77 | */
78 | public function text($name) {
79 | return $this->addField(new TextField($name));
80 | }
81 |
82 | /**
83 | * @param $name
84 | *
85 | * @return FieldWrapper
86 | */
87 | public function boolean($name) {
88 | return $this->addField(new BooleanField($name));
89 | }
90 |
91 | /**
92 | * @return mixed
93 | */
94 | public function getFields() {
95 | return $this->fields;
96 | }
97 |
98 | /**
99 | * @return array
100 | */
101 | public function getPrimary() {
102 | return array_filter(array_map(function (FieldWrapper $field) {
103 | return $field->isPrimary() ? $field->getName() : false;
104 | }, $this->getFields()));
105 | }
106 |
107 | /**
108 | * @return array
109 | */
110 | public function getAutoincrement() {
111 | return array_filter(array_map(function (FieldWrapper $field) {
112 | return $field->isAutoincrement() ? $field->getName() : false;
113 | }, $this->getFields()));
114 | }
115 |
116 | /**
117 | * @return array
118 | */
119 | public function getPreparedFields() {
120 | return array_map(function (FieldWrapper $field) {
121 | return $field->getField();
122 | }, $this->getFields());
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/lib/builder/entity/userfield.php:
--------------------------------------------------------------------------------
1 | 'name', 'en' => 'name']
14 | * @method UserField listLabel(array $value) - ['ru' => 'name', 'en' => 'name']
15 | * @method UserField filterLabel(array $value) - ['ru' => 'name', 'en' => 'name']
16 | * @method UserField settings(array $value)
17 | * @method UserField sort(int $value)
18 | *
19 | * @package WS\ReduceMigrations\Builder\Entity
20 | */
21 | class UserField extends Base {
22 |
23 | const TYPE_VIDEO = 'video';
24 | const TYPE_HLBLOCK = 'hlblock';
25 | const TYPE_STRING = 'string';
26 | const TYPE_INTEGER = 'integer';
27 | const TYPE_DOUBLE = 'double';
28 | const TYPE_DATETIME = 'datetime';
29 | const TYPE_DATE = 'date';
30 | const TYPE_BOOLEAN = 'boolean';
31 | const TYPE_FILE = 'file';
32 | const TYPE_ENUMERATION = 'enumeration';
33 | const TYPE_IBLOCK_SECTION = 'iblock_section';
34 | const TYPE_IBLOCK_ELEMENT = 'iblock_element';
35 | const TYPE_STRING_FORMATTED = 'string_formatted';
36 | const TYPE_VOTE = 'vote';
37 | private $enumVariants;
38 | private $id;
39 |
40 | public function __construct($code) {
41 | $this->code(strtoupper($code));
42 | $this->enumVariants = array();
43 | }
44 |
45 | public function getMap() {
46 | return array(
47 | 'code' => 'FIELD_NAME',
48 | 'entityId' => 'ENTITY_ID',
49 | 'type' => 'USER_TYPE_ID',
50 | 'xmlId' => 'XML_ID',
51 | 'sort' => 'SORT',
52 | 'multiple' => 'MULTIPLE',
53 | 'required' => 'MANDATORY',
54 | 'showInFilter' => 'SHOW_FILTER',
55 | 'showInList' => 'SHOW_IN_LIST',
56 | 'editInList' => 'EDIT_IN_LIST',
57 | 'searchable' => 'IS_SEARCHABLE',
58 | 'editFormLabel' => 'EDIT_FORM_LABEL',
59 | 'listLabel' => 'LIST_COLUMN_LABEL',
60 | 'filterLabel' => 'LIST_FILTER_LABEL',
61 | 'settings' => 'SETTINGS',
62 | );
63 | }
64 |
65 | /**
66 | * @param int $id
67 | * @return UserField
68 | */
69 | public function setId($id) {
70 | $this->id = $id;
71 | return $this;
72 | }
73 |
74 | /**
75 | * @return int
76 | */
77 | public function getId() {
78 | return $this->id;
79 | }
80 |
81 | /**
82 | * @param $label
83 | *
84 | * @return UserField $this
85 | */
86 | public function label($label) {
87 | $this->listLabel($label);
88 | $this->editFormLabel($label);
89 | $this->filterLabel($label);
90 | return $this;
91 | }
92 | /**
93 | * @param bool $multiple
94 | * @return UserField
95 | */
96 | public function multiple($multiple) {
97 | $this->setAttribute('MULTIPLE', $multiple ? 'Y' : 'N');
98 | return $this;
99 | }
100 |
101 | /**
102 | * @param bool $required
103 | * @return UserField
104 | */
105 | public function required($required) {
106 | $this->setAttribute('MANDATORY', $required ? 'Y' : 'N');
107 | return $this;
108 | }
109 |
110 | /**
111 | * @param bool $showInFilter
112 | * @return UserField
113 | */
114 | public function showInFilter($showInFilter) {
115 | $this->setAttribute('SHOW_FILTER', $showInFilter ? 'Y' : 'N');
116 | return $this;
117 | }
118 |
119 | /**
120 | * @param bool $showInList
121 | * @return UserField
122 | */
123 | public function showInList($showInList) {
124 | $this->setAttribute('SHOW_IN_LIST', $showInList ? 'Y' : 'N');
125 | return $this;
126 | }
127 |
128 | /**
129 | * @param bool $editInList
130 | * @return UserField
131 | */
132 | public function editInList($editInList) {
133 | $this->setAttribute('EDIT_IN_LIST', $editInList ? 'Y' : 'N');
134 | return $this;
135 | }
136 |
137 | /**
138 | * @param bool $searchable
139 | * @return UserField
140 | */
141 | public function searchable($searchable) {
142 | $this->setAttribute('IS_SEARCHABLE', $searchable ? 'Y' : 'N');
143 | return $this;
144 | }
145 |
146 | /**
147 | * @param $name
148 | * @return EnumVariant
149 | */
150 | public function addEnum($name) {
151 | $variant = new EnumVariant($name);
152 | $this->enumVariants[] = $variant;
153 | return $variant;
154 | }
155 |
156 | /**
157 | * @param $name
158 | * @return EnumVariant
159 | */
160 | public function updateEnum($name) {
161 | $data = $this->findEnum($name);
162 | $variant = new EnumVariant($name, $data);
163 | $variant->markClean();
164 | $this->enumVariants[] = $variant;
165 | return $variant;
166 | }
167 |
168 | /**
169 | * @param $name
170 | * @return UserField
171 | */
172 | public function removeEnum($name) {
173 | $data = $this->findEnum($name);
174 | $variant = new EnumVariant($name, $data);
175 | $variant->markDeleted();
176 | $this->enumVariants[] = $variant;
177 | return $this;
178 | }
179 |
180 | /**
181 | * @return EnumVariant[]
182 | */
183 | public function getEnumVariants() {
184 | return $this->enumVariants;
185 | }
186 |
187 | /**
188 | * @param $name
189 | * @return array
190 | * @throws BuilderException
191 | */
192 | private function findEnum($name) {
193 | if (!$this->getId()) {
194 | throw new BuilderException('Save Field before update enum');
195 | }
196 | $res = \CUserFieldEnum::GetList(null, array(
197 | 'USER_FIELD_ID' => $this->getId(),
198 | 'VALUE' => $name,
199 | ))->Fetch();
200 | if (empty($res)) {
201 | throw new BuilderException("Enum for `$name` not found");
202 | }
203 | return $res;
204 | }
205 |
206 | }
--------------------------------------------------------------------------------
/lib/builder/eventsbuilder.php:
--------------------------------------------------------------------------------
1 | commit($eventType);
21 | return $eventType;
22 | }
23 |
24 | /**
25 | * @param string $type
26 | * @param string $lid
27 | * @param \Closure $callback
28 | * @return EventType
29 | * @throws BuilderException
30 | */
31 | public function updateEventType($type, $lid, $callback) {
32 | $data = $this->findEventType($type, $lid);
33 | $eventType = new EventType($data['EVENT_NAME'], $data['LID']);
34 | $eventType->setId($data['ID']);
35 | $eventType->markClean();
36 | $callback($eventType);
37 | $this->commit($eventType);
38 | return $eventType;
39 | }
40 |
41 | /**
42 | * @param EventType $eventType
43 | *
44 | * @throws BuilderException
45 | */
46 | public function commit($eventType) {
47 | global $DB;
48 | $DB->StartTransaction();
49 | try {
50 | $this->commitEventType($eventType);
51 | } catch (\Exception $e) {
52 | $DB->Rollback();
53 | throw new BuilderException($e->getMessage());
54 | }
55 | $DB->Commit();
56 | }
57 |
58 | /**
59 | * @param $type
60 | * @param $lid
61 | * @return array
62 | * @throws BuilderException
63 | */
64 | private function findEventType($type, $lid) {
65 | $data = \CEventType::GetList(array(
66 | 'TYPE_ID' => $type,
67 | 'LID' => $lid
68 | ))->Fetch();
69 | if (empty($data)) {
70 | throw new BuilderException("EventType '{$type}' not found for lid '{$lid}'");
71 | }
72 | return $data;
73 | }
74 |
75 | /**
76 | * @param EventType $eventType
77 | *
78 | * @throws BuilderException
79 | */
80 | private function commitEventType($eventType) {
81 | global $APPLICATION;
82 |
83 | $gw = new \CEventType();
84 | if ($eventType->getId() > 0) {
85 | if ($eventType->isDirty()) {
86 | $result = $gw->Update(array('ID' => $eventType->getId()), $eventType->getData());
87 | if (!$result) {
88 | throw new BuilderException('EventType update failed with error: ' . $APPLICATION->GetException()->GetString());
89 | }
90 | }
91 |
92 | } else {
93 | $result = $gw->Add($eventType->getData());
94 | if (!$result) {
95 | throw new BuilderException('EventType add failed with error: ' . $APPLICATION->GetException()->GetString());
96 | }
97 | $eventType->setId($result);
98 | }
99 | $this->commitEventMessages($eventType->getEventName(), $eventType->getEventMessages());
100 | }
101 |
102 | /**
103 | * @param string $eventName
104 | * @param EventMessage[] $eventMessages
105 | *
106 | * @throws BuilderException
107 | */
108 | private function commitEventMessages($eventName, $eventMessages) {
109 | global $APPLICATION;
110 |
111 | $gw = new \CEventMessage();
112 | foreach ($eventMessages as $message) {
113 | if ($message->getId() > 0) {
114 | if ($message->isRemoved() && !$gw->Delete($message->getId())) {
115 | throw new BuilderException("EventType wasn't deleted: ". $APPLICATION->GetException()->GetString());
116 | }
117 | if ($message->isDirty() && !$gw->Update($message->getId(), $message->getData())) {
118 | throw new BuilderException("EventType wasn't updated: ". $APPLICATION->GetException()->GetString());
119 | }
120 | } else {
121 | $id = $gw->Add(array_merge(
122 | $message->getData(),
123 | array('EVENT_NAME' => $eventName)
124 | ));
125 | if (!$id) {
126 | throw new BuilderException("EventMessage add failed with error: " . $APPLICATION->GetException()->GetString());
127 | }
128 | $message->setId($id);
129 | }
130 |
131 | }
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/lib/builder/formbuilder.php:
--------------------------------------------------------------------------------
1 | commit($form);
27 | return $form;
28 | }
29 |
30 | /**
31 | * @param string $sid
32 | * @param \Closure $callback
33 | * @return Form
34 | * @throws BuilderException
35 | */
36 | public function updateForm($sid, $callback) {
37 | $formData = $this->findForm($sid);
38 | $form = new Form($formData['NAME'], $sid);
39 | $form->setId($formData['ID']);
40 | $form->markClean();
41 | $callback($form);
42 | $this->commit($form);
43 | return $form;
44 | }
45 |
46 | /**
47 | * @param string $sid
48 | * @return boolean
49 | */
50 | public function removeForm($sid) {
51 | $formData = $this->findForm($sid);
52 | if (!$formData['ID']) {
53 | return false;
54 | }
55 | return \CForm::Delete($formData['ID']);
56 | }
57 |
58 | /**
59 | * @param Form $form
60 | * @throws BuilderException
61 | */
62 | protected function commit($form) {
63 | global $DB;
64 | $DB->StartTransaction();
65 | try {
66 | $this->commitForm($form);
67 | $this->commitFields($form);
68 | $this->commitStatuses($form);
69 | } catch (\Exception $e) {
70 | $DB->Rollback();
71 | throw new BuilderException($e->getMessage());
72 | }
73 | $DB->Commit();
74 | }
75 |
76 | /**
77 | * @param Form $form
78 | * @throws BuilderException
79 | */
80 | private function commitForm($form) {
81 | global $strError;
82 | if (!$form->isDirty()) {
83 | return ;
84 | }
85 | $formId = \CForm::Set($form->getData(), $form->getId(), 'N');
86 | if (!$formId) {
87 | throw new BuilderException("Form wasn't saved. " . $strError);
88 | }
89 | $form->setId($formId);
90 | }
91 |
92 | /**
93 | * @param Form $form
94 | * @throws BuilderException
95 | */
96 | private function commitFields($form) {
97 | global $strError;
98 | $gw = new \CFormField();
99 | foreach ($form->getFields() as $field) {
100 | if ($field->isDirty()) {
101 | $field->setAttribute('FORM_ID', $form->getId());
102 | $saveData = $field->getData();
103 | $fieldId = $gw->Set($saveData, $field->getId(), 'N', 'Y');
104 | if (!$fieldId) {
105 | throw new BuilderException("Field '{$field->getAttribute('SID')}' wasn't saved. " . $strError);
106 | }
107 | $field->setId($fieldId);
108 | }
109 |
110 | $this->commitAnswers($field);
111 | }
112 | }
113 |
114 | /**
115 | * @param FormField $field
116 | * @throws BuilderException
117 | */
118 | private function commitAnswers($field) {
119 | global $strError;
120 | $gw = new \CFormAnswer();
121 | foreach ($field->getAnswers() as $answer) {
122 | if ($answer->needDelete()) {
123 | if (!$gw->Delete($answer->getId())) {
124 | throw new BuilderException("Can't delete '{$answer->getAttribute('MESSAGE')}'. ". $strError);
125 | }
126 | }
127 | $answer->setAttribute('QUESTION_ID', $field->getId());
128 | $data = $answer->getData();
129 | if ($answer->isDirty() && !$gw->Set($data, $answer->getId())) {
130 | throw new BuilderException("Answer wasn't saved. " . $strError);
131 | }
132 | }
133 | }
134 |
135 | /**
136 | * @param Form $form
137 | * @throws BuilderException
138 | */
139 | private function commitStatuses($form) {
140 | global $strError;
141 | $gw = new \CFormStatus();
142 | foreach ($form->getStatuses() as $status) {
143 | if (!$form->isDirty()) {
144 | continue;
145 | }
146 | $status->setAttribute('FORM_ID', $form->getId());
147 | $saveData = $status->getData();
148 | $statusId = $gw->Set($saveData, $status->getId(), 'N');
149 | if (!$statusId) {
150 | throw new BuilderException("Field '{$status->getAttribute('TITLE')}' wasn't saved. " . $strError);
151 | }
152 | $status->setId($statusId);
153 | }
154 | }
155 |
156 | /**
157 | * @param $sid
158 | * @return array
159 | * @throws BuilderException
160 | */
161 | private function findForm($sid) {
162 | $data = \CForm::GetList($by = 'ID', $order = 'ASC', array(
163 | 'SID' => $sid
164 | ), $isFiltered = false)->Fetch();
165 |
166 | if (!$data) {
167 | throw new BuilderException("Form '{$sid}' not found");
168 | }
169 |
170 | return $data;
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/lib/builder/highloadblockbuilder.php:
--------------------------------------------------------------------------------
1 | commit($highLoadBlock);
29 | return $highLoadBlock;
30 | }
31 |
32 | /**
33 | * @param string $tableName
34 | * @param \Closure $callback
35 | *
36 | * @return HighLoadBlock
37 | * @throws BuilderException
38 | */
39 | public function updateHLBlock($tableName, $callback) {
40 | $block = $this->findTable($tableName);
41 | $highLoadBlock = new HighLoadBlock($block['NAME'], $tableName, $block['ID']);
42 | $highLoadBlock->markClean();
43 | $callback($highLoadBlock);
44 | $this->commit($highLoadBlock);
45 | return $highLoadBlock;
46 | }
47 |
48 | /**
49 | * @var HighLoadBlock $highLoadBlock
50 | * @throws BuilderException
51 | */
52 | private function commit($highLoadBlock) {
53 | global $DB;
54 | $DB->StartTransaction();
55 | try {
56 | $this->commitHighLoadBlock($highLoadBlock);
57 | $this->commitFields($highLoadBlock);
58 | } catch (BuilderException $e) {
59 | $DB->Rollback();
60 | throw new BuilderException($e->getMessage());
61 | }
62 | $DB->Commit();
63 | }
64 |
65 | /**
66 | * @param $tableName
67 | * @return array|false
68 | * @throws BuilderException
69 | * @throws \Bitrix\Main\ArgumentException
70 | */
71 | public function findTable($tableName) {
72 | $hbRes = HighloadBlockTable::getList(array(
73 | 'filter' => array(
74 | 'TABLE_NAME' => $tableName
75 | )
76 | ));
77 | if (!($table = $hbRes->fetch())){
78 | throw new BuilderException('Cant find block by table name `'.$tableName.'` ');
79 | }
80 | return $table;
81 | }
82 |
83 | /**
84 | * @var HighLoadBlock $highLoadBlock
85 | * @throws BuilderException
86 | * @throws \Bitrix\Main\SystemException
87 | */
88 | private function commitHighLoadBlock($highLoadBlock) {
89 | $isSuccess = true;
90 | if (!$highLoadBlock->getId()) {
91 | $hbRes = HighloadBlockTable::add($highLoadBlock->getData());
92 | $isSuccess = $hbRes->isSuccess();
93 | $highLoadBlock->setId($hbRes->getId());
94 | } elseif ($highLoadBlock->isDirty()) {
95 | $hbRes = HighloadBlockTable::update(
96 | $highLoadBlock->getId(),
97 | $highLoadBlock->getData()
98 | );
99 | $isSuccess = $hbRes->isSuccess();
100 | }
101 | if (!$isSuccess) {
102 | throw new BuilderException($highLoadBlock->getAttribute('TABLE_NAME') . ' ' . implode(', ', $hbRes->getErrorMessages()));
103 | }
104 | }
105 |
106 | /**
107 | * @var HighLoadBlock $highLoadBlock
108 | * @throws BuilderException
109 | */
110 | private function commitFields($highLoadBlock) {
111 | $this->commitUserFields($highLoadBlock->getFields(), "HLBLOCK_{$highLoadBlock->getId()}");
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/builder/iblockpointer.php:
--------------------------------------------------------------------------------
1 | type = $type;
21 | $this->value = $value;
22 | }
23 |
24 | /**
25 | * @param $name
26 | * @return IblockPointer
27 | */
28 | public static function byName($name) {
29 | return new static(self::TYPE_NAME, $name);
30 | }
31 |
32 | /**
33 | * @param $id
34 | * @return IblockPointer
35 | */
36 | public static function byId($id) {
37 | return new static(self::TYPE_ID, $id);
38 | }
39 |
40 | /**
41 | * @param $code
42 | * @return IblockPointer
43 | */
44 | public static function byCode($code) {
45 | return new static(self::TYPE_CODE, $code);
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getType() {
52 | return $this->type;
53 | }
54 |
55 | /**
56 | * @return mixed
57 | */
58 | public function getValue() {
59 | return $this->value;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/builder/tablebuilder.php:
--------------------------------------------------------------------------------
1 | createTable($table);
21 | return $table;
22 | }
23 |
24 | /**
25 | * @param $tableName
26 | */
27 | public function drop($tableName) {
28 | $database = Application::getConnection();
29 | if (!$database->isTableExists($tableName)) {
30 | return;
31 | }
32 | $database->dropTable($tableName);
33 | }
34 |
35 | /**
36 | * @param $tableName
37 | * @param $columnName
38 | */
39 | public function dropColumn($tableName, $columnName) {
40 | $database = Application::getConnection();
41 | if (!$database->isTableExists($tableName)) {
42 | return;
43 | }
44 | $database->dropColumn($tableName, $columnName);
45 | }
46 |
47 | /**
48 | * @param $tableName
49 | * @param $columnName
50 | * @param $type
51 | */
52 | public function addColumn($tableName, $columnName, $type) {
53 | $database = Application::getConnection();
54 | if (!$database->isTableExists($tableName)) {
55 | return;
56 | }
57 | $columnName = strtoupper($columnName);
58 | $type = strtoupper($type);
59 | $sqlHelper = $database->getSqlHelper();
60 | $database
61 | ->query('ALTER TABLE '. $sqlHelper->quote($tableName).' ADD '.$sqlHelper->quote($columnName) . ' ' . $type);
62 | }
63 |
64 | /**
65 | * @param $tableName
66 | * @param $columnName
67 | *
68 | * @return bool
69 | */
70 | public function isColumnExists($tableName, $columnName) {
71 | $database = Application::getConnection();
72 |
73 | $field = $database->getTableField($tableName, $columnName);
74 |
75 | return $field !== null;
76 | }
77 |
78 | /**
79 | * @param Table $table
80 | */
81 | private function createTable($table) {
82 | $database = Application::getConnection();
83 | $database
84 | ->createTable($table->name, $table->getPreparedFields(), $table->getPrimary(), $table->getAutoincrement());
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/lib/builder/traits/containuserfieldstrait.php:
--------------------------------------------------------------------------------
1 | user_fields[] = $field;
27 |
28 | return $field;
29 | }
30 |
31 | /**
32 | * @param string $code
33 | * @param string $entity_id
34 | * @return UserField
35 | * @throws BuilderException
36 | */
37 | private function updateUserField($code, $entity_id) {
38 | $data = $this->findUserField($code, $entity_id);
39 | $field = new UserField($code);
40 | $field->setId($data['ID']);
41 | $field->markClean();
42 | $this->user_fields[] = $field;
43 |
44 | return $field;
45 | }
46 |
47 | /**
48 | * @param string $code
49 | * @param string $entity_id
50 | * @return array
51 | * @throws BuilderException
52 | */
53 | private function findUserField($code, $entity_id) {
54 | $field = \CUserTypeEntity::GetList(null, array(
55 | 'FIELD_NAME' => $code,
56 | 'ENTITY_ID' => $entity_id,
57 | ))->Fetch();
58 |
59 | if (empty($field)) {
60 | throw new BuilderException("Field for `$code` not found");
61 | }
62 |
63 | return $field;
64 | }
65 |
66 | /**
67 | * @return UserField[]
68 | */
69 | private function getUserFields() {
70 | return $this->user_fields;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/builder/traits/operateuserfieldentitytrait.php:
--------------------------------------------------------------------------------
1 | getId() > 0) {
29 | $field->isDirty() && $res = $gw->Update($field->getId(), $field->getData());
30 | } else {
31 | $res = $gw->Add(array_merge($field->getData(), array(
32 | 'ENTITY_ID' => $entity_id,
33 | )));
34 | if ($res) {
35 | $field->setId($res);
36 | }
37 | }
38 | if (!$res) {
39 | throw new BuilderException($APPLICATION->GetException()->GetString());
40 | }
41 |
42 | $this->commitUserFieldEnum($field);
43 | }
44 | }
45 |
46 | /**
47 | * @param UserField $field
48 | * @throws BuilderException
49 | */
50 | private function commitUserFieldEnum($field) {
51 | global $APPLICATION;
52 | $obEnum = new \CUserFieldEnum;
53 | $values = array();
54 | foreach ($field->getEnumVariants() as $key => $variant) {
55 | $key = 'n' . $key;
56 | if ($variant->getId() > 0) {
57 | $key = $variant->getId();
58 | }
59 | $values[$key] = $variant->getData();
60 | }
61 | if (empty($values)) {
62 | return;
63 | }
64 | if (!$obEnum->SetEnumValues($field->getId(), $values)) {
65 | throw new BuilderException($APPLICATION->GetException()->GetString());
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/collection/migrationcollection.php:
--------------------------------------------------------------------------------
1 | getName());
14 | if (!class_exists($fileClass)) {
15 | include $file->getPath();
16 | }
17 |
18 | if (!is_subclass_of($fileClass, '\WS\ReduceMigrations\Scenario\ScriptScenario')) {
19 | continue;
20 | }
21 | if (!$fileClass::isValid()) {
22 | continue;
23 | }
24 | $this->elements[] = $fileClass;
25 | }
26 | }
27 |
28 | /**
29 | * @return float|int
30 | */
31 | public function getApproximateTime() {
32 | $time = 0;
33 | foreach ($this->elements as $element) {
34 | $time += (double)$element::approximatelyTime();
35 | }
36 | return $time;
37 | }
38 |
39 | /**
40 | * @return array
41 | */
42 | public function groupByPriority() {
43 | $elements = array();
44 | $priorities = ScriptScenario::getPriorities();
45 | foreach ($priorities as $priority) {
46 | $elements[$priority] = array();
47 | }
48 |
49 | foreach ($this->elements as $key => $element) {
50 | $elements[$element::priority()][] = $element;
51 | }
52 |
53 | return array_filter($elements);
54 | }
55 |
56 | /**
57 | * @return ScriptScenario[]
58 | */
59 | public function toArray() {
60 | $migrations = $this->groupByPriority();
61 | $result = array();
62 | array_walk_recursive($migrations, function($item) use (& $result) {
63 | $result[] = $item;
64 | });
65 | return $result;
66 | }
67 |
68 | /**
69 | * @return int
70 | */
71 | public function count() {
72 | return count($this->elements);
73 | }
74 |
75 | /**
76 | * @param $migrationHash
77 | *
78 | * @return ScriptScenario[]
79 | */
80 | public function findByHash($migrationHash) {
81 | $list = array();
82 | foreach ($this->elements as $element) {
83 | if (strpos($element::hash(), $migrationHash) !== 0) {
84 | continue;
85 | }
86 | $list[] = $element;
87 | }
88 | return $list;
89 | }
90 |
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/lib/console/command/applycommand.php:
--------------------------------------------------------------------------------
1 | force = isset($params[self::FLAG_FORCE]) ? $params[self::FLAG_FORCE] : false;
22 | $this->skipOptional = isset($params[self::FLAG_SKIP_OPTIONAL]) ? $params[self::FLAG_SKIP_OPTIONAL] : false;
23 | $this->migrationHash = isset($params[0]) ? $params[0] : null;
24 |
25 | }
26 |
27 | public function execute($callback = false) {
28 | $listCommand = new ListCommand($this->console, array($this->migrationHash));
29 | $listCommand->execute();
30 |
31 | $notAppliedScenarios = $this->module->getNotAppliedScenarios();
32 | $count = $notAppliedScenarios->count();
33 |
34 | if ($count == 0) {
35 | return;
36 | }
37 | if ($this->migrationHash) {
38 | $hasByHash = false;
39 | foreach ($notAppliedScenarios->toArray() as $notAppliedScenario) {
40 | if (strpos($notAppliedScenario::getShortenedHash(), $this->migrationHash) !== false) {
41 | $hasByHash = true;
42 | break;
43 | }
44 | }
45 | if (!$hasByHash) {
46 | return;
47 | }
48 | }
49 |
50 | $this->confirmAction();
51 |
52 | $this->console
53 | ->printLine("\nApplying new migrations started...\n", Console::OUTPUT_PROGRESS);
54 |
55 | $timer = new Timer();
56 | $timer->start();
57 | try {
58 | if ($this->migrationHash) {
59 | $count = 1;
60 | $this->module
61 | ->applyMigrationByHash($this->migrationHash, $callback);
62 | } else {
63 | $count = (int)$this->module
64 | ->applyMigrations($this->skipOptional, $callback);
65 | }
66 | } catch (\Exception $e) {
67 | throw new ConsoleException($e->getMessage());
68 | }
69 |
70 | $timer->stop();
71 | $time = $this->console->formatTime($timer->getTime());
72 | $this->console
73 | ->printLine("Apply action finished! $count items, time {$time}", Console::OUTPUT_PROGRESS);
74 | }
75 |
76 | private function confirmAction() {
77 | if ($this->force) {
78 | return true;
79 | }
80 |
81 | $this->console
82 | ->printLine('Are you sure? (yes|no):');
83 |
84 | $answer = $this->console->readLine();
85 |
86 | if ($answer !== self::CONFIRM_WORD) {
87 | exit();
88 | }
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/lib/console/command/basecommand.php:
--------------------------------------------------------------------------------
1 | console = $console;
17 | $this->initParams($params);
18 | $this->module = Module::getInstance();
19 | }
20 |
21 | /**
22 | * @return string
23 | */
24 | static public function className() {
25 | return get_called_class();
26 | }
27 | protected function initParams($params) {}
28 |
29 | abstract public function execute($callback = false);
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/lib/console/command/createscenariocommand.php:
--------------------------------------------------------------------------------
1 | ScriptScenario::PRIORITY_HIGH,
25 | self::MEDIUM_PRIORITY_SHORTCUT => ScriptScenario::PRIORITY_MEDIUM,
26 | self::OPTIONAL_PRIORITY_SHORTCUT => ScriptScenario::PRIORITY_OPTIONAL,
27 | );
28 | }
29 |
30 | protected function initParams($params) {
31 | $this->name = isset($params[self::PARAM_NAME]) ? trim($params[self::PARAM_NAME]) : false;
32 | $this->priority = isset($params[self::PARAM_PRIORITY]) ? trim($params[self::PARAM_PRIORITY]) : false;
33 | $this->time = isset($params[self::PARAM_TIME]) ? trim($params[self::PARAM_TIME]) : false;
34 | }
35 |
36 | private function getName() {
37 | if ($this->name) {
38 | return $this->name;
39 | }
40 | $this->console
41 | ->printLine('Enter name:');
42 | $name = $this->console
43 | ->readLine();
44 | while (!strlen(trim($name))) {
45 | $this->console
46 | ->printLine("Name mustn't be empty. Enter name:");
47 | $name = $this->console
48 | ->readLine();
49 | }
50 | return $name;
51 | }
52 |
53 | private function getPriority() {
54 | $priority = $this->normalizePriority($this->priority);
55 | while (!$priority) {
56 | $this->console
57 | ->printLine('Enter priority(h - high, m - medium, o - optional):');
58 | $priority = $this->normalizePriority($this->console
59 | ->readLine());
60 | }
61 | return $priority;
62 | }
63 |
64 | private function normalizePriority($priority) {
65 | $priorities = $this->availablePriorities();
66 | if ($priorities[$priority]) {
67 | return $priorities[$priority];
68 | }
69 | if (in_array($priority, $priorities, true)) {
70 | return $priority;
71 | }
72 |
73 | return false;
74 | }
75 |
76 | public function execute($callback = false) {
77 | try {
78 | $fileName = $this->module->createScenario($this->prepareName($this->getName()), $this->getPriority(), (int)$this->time);
79 | } catch (\Exception $e) {
80 | $this->console->printLine('An error occurred saving file', Console::OUTPUT_ERROR);
81 | $this->console->printLine($e->getMessage());
82 | return;
83 | }
84 | $this->console->printLine($fileName, Console::OUTPUT_SUCCESS);
85 | }
86 |
87 | public function prepareName($name) {
88 | /* @var \CMain */
89 | global $APPLICATION;
90 | $name = $APPLICATION->ConvertCharset($name, mb_detect_encoding($name), LANG_CHARSET);
91 | return $name;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/lib/console/command/history.php:
--------------------------------------------------------------------------------
1 | count = isset($params[0]) ? $params[0] : false;
15 | }
16 |
17 | public function execute($callback = false) {
18 | $lastSetupLog = \WS\ReduceMigrations\Module::getInstance()->getLastSetupLog();
19 |
20 | if (!$lastSetupLog) {
21 | throw new ConsoleException('Nothing to show');
22 | }
23 |
24 | $table = new ConsoleTable();
25 | $table->setCharset(LANG_CHARSET);
26 |
27 | $table->setHeaders(array(
28 | 'Date', 'Name', 'Hash', 'Duration'
29 | ));
30 | $table->setCellsLength(array(
31 | 19, 80, 10, 10
32 | ));
33 |
34 | if (!$this->count) {
35 | $logs = $lastSetupLog->getAppliedLogs();
36 | } else {
37 | $logs = AppliedChangesLogModel::find(array(
38 | 'order' => array('id' => 'desc'),
39 | 'limit' => $this->count
40 | ));
41 | }
42 |
43 | $count = 0;
44 | $commonDuration = 0;
45 | $setupPaddings = $this->getVerticalPaddingsForSetups($logs);
46 | $currentSetupId = 0;
47 | /** @var AppliedChangesLogModel $log */
48 | foreach ($logs as $log) {
49 | if ($currentSetupId == 0) {
50 | $currentSetupId = $log->setupLogId;
51 | }
52 | if ($currentSetupId != $log->setupLogId) {
53 | $currentSetupId = $log->setupLogId;
54 | $table->addRow();
55 | }
56 | $date = '';
57 | if ($setupPaddings[$log->setupLogId] == 0) {
58 | $date = $log->getDate()->format('d.m.Y H:i:s');
59 | }
60 | $setupPaddings[$log->setupLogId]--;
61 |
62 | $duration = $this->console->formatTime($log->getTime());
63 | $log->isFailed() && $duration = "failed";
64 | $log->isSkipped() && $duration = "skipped";
65 |
66 | $table->addRow(array(
67 | $date, $log->getName(), $log->getHash(), $duration
68 | ));
69 | if ($log->isFailed()) {
70 | $table->addRow(array('', 'Error: '.$log->getErrorMessage(), '', ''));
71 | }
72 | $count++;
73 | $commonDuration += $log->getTime();
74 | }
75 | $table->addRow(array(
76 | '-------------------', '----------------------------------------', '----------', '---------'
77 | ));
78 | $table->addRow(array(
79 | '', 'Total: '.$count, '', $this->console->formatTime($commonDuration)
80 | ));
81 |
82 | $this->console->printLine("{$count} Last applied migrations:");
83 | $this->console->printLine($table->getTable());
84 | }
85 |
86 |
87 | private function getVerticalPaddingsForSetups($logs) {
88 | $arTrackSetup = array();
89 | foreach ($logs as $log) {
90 | $arTrackSetup[$log->setupLogId]++;
91 | }
92 | foreach ($arTrackSetup as & $countRecords) {
93 | $countRecords = (int) (($countRecords - 1) / 2);
94 | }
95 | return $arTrackSetup;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/lib/console/command/listcommand.php:
--------------------------------------------------------------------------------
1 | registeredFixes = array();
23 | $this->localization = Module::getInstance()->getLocalization('admin')->fork('cli');
24 | $this->hash = $params[0];
25 | }
26 |
27 | public function execute($callback = false) {
28 | $has = false;
29 | $notAppliedScenarios = $this->module->getNotAppliedScenarios();
30 | foreach ($notAppliedScenarios->groupByPriority() as $priority => $list) {
31 | /** @var ScriptScenario $notAppliedScenario */
32 | foreach ($list as $notAppliedScenario) {
33 | if ($this->hash && strpos($notAppliedScenario::hash(), $this->hash) === false) {
34 | continue;
35 | }
36 |
37 | $this->registerFix($priority, $notAppliedScenario);
38 | $has = true;
39 | }
40 | }
41 | !$has && $this->console->printLine("Nothing to apply\n", Console::OUTPUT_SUCCESS);
42 | $has && $this->printRegisteredFixes($notAppliedScenarios->getApproximateTime());
43 | }
44 |
45 | /**
46 | * @param $priority
47 | * @param ScriptScenario $notAppliedScenario
48 | */
49 | private function registerFix($priority, $notAppliedScenario) {
50 | $this->registeredFixes[$priority][] = array(
51 | 'name' => $notAppliedScenario::name(),
52 | 'hash' => $notAppliedScenario::getShortenedHash(),
53 | 'time' => $this->console->formatTime($notAppliedScenario::approximatelyTime()),
54 | );
55 | }
56 |
57 | private function printRegisteredFixes($time) {
58 | $table = new ConsoleTable();
59 | $table->setCharset(LANG_CHARSET);
60 |
61 | $table->setHeaders(array(
62 | 'Priority', 'Name', 'Hash', '~Duration'
63 | ));
64 |
65 | $table->setCellsLength(array(10, 80, 10, 10));
66 |
67 | $count = 0;
68 | foreach ($this->registeredFixes as $priority => $fixList) {
69 | $priorityPos = (int) ((count($fixList) - 1) / 2);
70 |
71 | $fixList = array_values($fixList);
72 | foreach ($fixList as $k => $fix) {
73 | $table->addRow(array(
74 | $k == $priorityPos ? $priority : '', $fix['name'], $fix['hash'], $fix['time']
75 | ));
76 | $count++;
77 | }
78 | $table->addRow(array());
79 | }
80 | $table->addRow(array(
81 | '----------', '---------------------', '----------', '----------'
82 | ));
83 | $table->addRow(array(
84 | '', 'Total: '.$count, '', $this->console->formatTime($time)
85 | ));
86 | $this->console
87 | ->printLine('List of migrations:')
88 | ->printLine($table->getTable());
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/console/command/rollbackcommand.php:
--------------------------------------------------------------------------------
1 | timer = new Timer();
30 | }
31 |
32 | protected function initParams($params) {
33 | $this->migrationHash = isset($params[0]) ? $params[0] : null;
34 | $this->count = isset($params[self::PARAM_COUNT]) ? (int)$params[self::PARAM_COUNT] : null;
35 | if ($this->count && $this->count < 0) {
36 | $this->count = 0;
37 | }
38 | $this->toHash = isset($params[self::PARAM_TO_HASH]) ? $params[self::PARAM_TO_HASH] : null;
39 | $this->type = $this->identifyType();
40 | }
41 |
42 | private function identifyType() {
43 | if ($this->migrationHash) {
44 | return self::TYPE_HASH;
45 | }
46 | if ($this->count) {
47 | return self::TYPE_COUNT;
48 | }
49 | if ($this->toHash) {
50 | return self::TYPE_TO_HASH;
51 | }
52 |
53 | return self::TYPE_LAST_BATCH;
54 | }
55 |
56 | public function execute($callback = false) {
57 |
58 | try {
59 | $this->rollback($callback);
60 | } catch (\Exception $e) {
61 | throw new ConsoleException($e->getMessage());
62 | }
63 |
64 | $this->timer->stop();
65 | $time = $this->console->formatTime($this->timer->getTime());
66 | $this->console
67 | ->printLine("Rollback action finished! Time $time", Console::OUTPUT_PROGRESS);
68 | }
69 |
70 | private function rollback($callback = false) {
71 | switch ($this->type) {
72 | case self::TYPE_HASH:
73 | $this->showBatch(AppliedChangesLogModel::findByHash($this->migrationHash));
74 | $this->confirm("Rollback migration with hash={$this->migrationHash}.");
75 | $this->timer->start();
76 | $this->module->rollbackByHash($this->migrationHash);
77 | break;
78 | case self::TYPE_COUNT:
79 | $this->showBatch(AppliedChangesLogModel::findLastFewMigrations($this->count));
80 | $this->confirm("Rollback last {$this->count} migrations.");
81 | $this->timer->start();
82 | $this->module->rollbackLastFewMigrations($this->count, $callback);
83 | break;
84 | case self::TYPE_TO_HASH:
85 | $this->showBatch(AppliedChangesLogModel::findToHash($this->toHash));
86 | $this->confirm("Rollback migrations to hash={$this->toHash}.");
87 | $this->timer->start();
88 | $this->module->rollbackToHash($this->toHash, $callback);
89 | break;
90 | case self::TYPE_LAST_BATCH:
91 | $this->showBatch(AppliedChangesLogModel::findLastBatch());
92 | $this->confirm('Rollback last batch.');
93 | $this->timer->start();
94 | $this->module->rollbackLastBatch($callback);
95 | break;
96 | }
97 |
98 | }
99 |
100 | private function confirm($message) {
101 | $this->console
102 | ->printLine($message . ' Are you sure? (yes|no):');
103 |
104 | $answer = $this->console
105 | ->readLine();
106 |
107 | if ($answer !== self::CONFIRM_WORD) {
108 | throw new ConsoleException('Operation cancelled');
109 | }
110 |
111 | $this->console
112 | ->printLine('Rollback action started...', Console::OUTPUT_PROGRESS);
113 | }
114 |
115 | /**
116 | * @param AppliedChangesLogModel[] $logs
117 | */
118 | private function showBatch($logs) {
119 | if (empty($logs)) {
120 | return;
121 | }
122 | $table = new ConsoleTable();
123 | $table->setCharset(LANG_CHARSET);
124 |
125 | $table->setHeaders(array(
126 | 'Date', 'Name', 'Hash', 'Status'
127 | ));
128 | $table->setCellsLength(array(
129 | 19, 80, 10, 10
130 | ));
131 | foreach ($logs as $log) {
132 | $status = 'successful';
133 | if ($log->isSkipped()) {
134 | $status = 'skipped';
135 | } elseif ($log->isFailed()) {
136 | $status = 'failed';
137 | }
138 | $table->addRow(array(
139 | $log->getDate()->format('d.m.Y H:i:s'), $log->getName(), $log->getHash(), $status
140 | ));
141 | }
142 | $table->addRow(array(
143 | '-------------------', '---------------------', '----------', '----------'
144 | ));
145 | $table->addRow(array(
146 | '', 'Total: '.count($logs)
147 | ));
148 | $this->console
149 | ->printLine('Migrations for rollback:')
150 | ->printLine($table->getTable());
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/lib/console/console.php:
--------------------------------------------------------------------------------
1 | ConvertCharsetArray($args, "UTF-8", LANG_CHARSET);
40 | $this->out = fopen('php://stdout', 'w');
41 | array_shift($args);
42 | $this->params = $args;
43 | $this->action = isset($this->params[0]) ? $this->params[0] : '--help';
44 | foreach ($args as $arg) {
45 | if ($arg == '--help') {
46 | $this->action = '--help';
47 | $index = array_search($this->action, $this->params);
48 | if ($index !== false) {
49 | unset($this->params[$index]);
50 | }
51 | array_unshift($this->params, $this->action);
52 | }
53 | }
54 | $this->successOutput = new Output('green');
55 | $this->errorOutput = new Output('red');
56 | $this->progressOutput = new Output('yellow');
57 | $this->defaultOutput = new Output();
58 | $this->timeFormatter = new TimeFormatter(array(
59 | 'minutes' => 'min',
60 | 'seconds' => 'sec'
61 | ));
62 |
63 | Module::getInstance()->setScenariosMessageOutput($this);
64 | }
65 |
66 | /**
67 | * @param $str
68 | * @param $type
69 | * @return Console
70 | */
71 | public function printLine($str, $type = false) {
72 | global $APPLICATION;
73 | $str = $APPLICATION->ConvertCharset($str, LANG_CHARSET, "UTF-8");
74 | $str = $this->colorize($str, $type);
75 | fwrite($this->out, $str . "\n");
76 | return $this;
77 | }
78 |
79 | public function println($str) {
80 | return $this->printInProgress($str);
81 | }
82 |
83 | /**
84 | * @param $str
85 | * @return Console
86 | */
87 | public function printError($str) {
88 | return $this->printLine($str, self::OUTPUT_ERROR);
89 | }
90 |
91 | /**
92 | * @param $str
93 | * @return Console
94 | */
95 | public function printInProgress($str) {
96 | return $this->printLine($str, self::OUTPUT_PROGRESS);
97 | }
98 |
99 | /**
100 | * @param $str
101 | * @return Console
102 | */
103 | public function printSuccess($str) {
104 | return $this->printLine($str, self::OUTPUT_SUCCESS);
105 | }
106 |
107 | public function colorize($str, $type = false) {
108 | if ($type) {
109 | $str = $this->getOutput($type)->colorize($str);
110 | }
111 | return $str;
112 | }
113 |
114 | public function readLine() {
115 | return trim(fgets(STDIN));
116 | }
117 |
118 | /**
119 | * @return BaseCommand
120 | * @throws ConsoleException
121 | */
122 | public function getCommand() {
123 | $commands = array(
124 | '--help' => HelpCommand::className(),
125 | 'list' => ListCommand::className(),
126 | 'apply' => ApplyCommand::className(),
127 | 'rollback' => RollbackCommand::className(),
128 | 'history' => History::className(),
129 | 'createScenario' => CreateScenarioCommand::className(),
130 | 'create' => CreateScenarioCommand::className(),
131 | );
132 | if (!$commands[$this->action]) {
133 | throw new ConsoleException("Action `{$this->action}` is not supported");
134 | }
135 | $params = $this->prepareParams($this->params);
136 | return new $commands[$this->action]($this, $params);
137 | }
138 |
139 | /**
140 | * @param $type
141 | * @return Output
142 | */
143 | private function getOutput($type) {
144 | switch ($type) {
145 | case 'success':
146 | return $this->successOutput;
147 | break;
148 | case 'error':
149 | return $this->errorOutput;
150 | break;
151 | case 'progress':
152 | return $this->progressOutput;
153 | break;
154 | default:;
155 | }
156 | return $this->defaultOutput;
157 | }
158 |
159 | /**
160 | * @param $params
161 | *
162 | * @return array
163 | */
164 | private function prepareParams($params) {
165 | array_shift($params);
166 | $namedParams = array();
167 | $positionalParams = array();
168 | foreach ($params as $param) {
169 | if (strpos($param, '-') === 0) {
170 | $param = explode('=', $param);
171 | $namedParams[$param[0]] = $param[1] ?: true;
172 | } else {
173 | $positionalParams[] = $param;
174 | }
175 | }
176 | $params = array_merge($positionalParams, $namedParams);
177 |
178 | return $params;
179 | }
180 |
181 | public function formatTime($time) {
182 | return $this->timeFormatter->format($time);
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/lib/console/consoleexception.php:
--------------------------------------------------------------------------------
1 | textColors();
12 | $this->color = isset($colors[$color]) ? $colors[$color] : $colors['default'];
13 | }
14 |
15 | public function textColors () {
16 | return array(
17 | 'black' => 30,
18 | 'red' => 31,
19 | 'green' => 32,
20 | 'yellow' => 33,
21 | 'blue' => 34,
22 | 'magenta' => 35,
23 | 'cyan' => 36,
24 | 'white' => 37,
25 | 'default' => 0
26 | );
27 | }
28 |
29 | public function colorize($text) {
30 | return chr(27) . "[{$this->color}m" . $text . chr(27) . "[0m";
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/lib/console/formatter/table.php:
--------------------------------------------------------------------------------
1 | title = $title;
22 | $this->console = $console;
23 | }
24 |
25 | /**
26 | * @param $row
27 | * @param $color
28 | */
29 | public function addColorRow($row, $color) {
30 | $currentRow = array();
31 | foreach ($row as $item) {
32 | $currentRow[] = $this->console->colorize($item, $color);
33 | }
34 | $this->rows[] = $currentRow;
35 | }
36 |
37 | public function addRow() {
38 | $this->rows[] = func_get_args();
39 | }
40 |
41 | public function __toString() {
42 | $result = '';
43 | $result .= $this->title . "\n";
44 | $maxLen = array();
45 | foreach ($this->rows as $row) {
46 | foreach ($row as $index => $value) {
47 | if (!$maxLen[$index]) {
48 | $maxLen[$index] = iconv_strlen($value);
49 | }
50 | if ($maxLen[$index] < iconv_strlen($value)) {
51 | $maxLen[$index] = iconv_strlen($value);
52 | }
53 | }
54 | }
55 |
56 | foreach ($this->rows as $row) {
57 | foreach ($row as $index => $value) {
58 | $result .= mb_str_pad($value, $maxLen[$index] + 3);
59 | }
60 | $result .= "\n";
61 | }
62 |
63 | return $result;
64 | }
65 | }
66 |
67 | function mb_str_pad($input, $pad_length, $pad_string=' ', $pad_type=STR_PAD_RIGHT) {
68 | if (function_exists('mb_strlen')) {
69 | $diff = strlen($input) - mb_strlen($input);
70 | return str_pad($input, $pad_length + $diff, $pad_string, $pad_type);
71 | }
72 | return str_pad($input, $pad_length, $pad_string, $pad_type);
73 | }
--------------------------------------------------------------------------------
/lib/console/runtimecounter.php:
--------------------------------------------------------------------------------
1 | start = microtime(true);
11 | $this->migrationCount = 0;
12 | $this->migrationNumbe = 0;
13 | }
14 | }
--------------------------------------------------------------------------------
/lib/console/runtimefixcounter.php:
--------------------------------------------------------------------------------
1 | time = 0;
17 | $this->activeFixName = '';
18 | $this->fixNames = array();
19 | $this->fixNumber = 0;
20 | $this->migrationCount = 0;
21 | }
22 |
23 | /**
24 | * @param AppliedChangesLogModel[] $list
25 | */
26 | public function setFixNamesByLogs($list) {
27 | foreach ($list as $log) {
28 | if ($log->isFailed() || $log->isSkipped()) {
29 | continue;
30 | }
31 | $this->migrationCount++;
32 | continue;
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/lib/dumbmessageoutput.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Entities;
7 |
8 | use Bitrix\Main\Entity\DataManager;
9 |
10 | class AppliedChangesLogTable extends DataManager {
11 | public static function className() {
12 | return get_called_class();
13 | }
14 |
15 | public static function getFilePath() {
16 | return __FILE__;
17 | }
18 |
19 | public static function getTableName() {
20 | return 'ws_reducemigrations_apply_changes_log';
21 | }
22 |
23 | public static function getMap() {
24 | return array(
25 | 'ID' => array(
26 | 'data_type' => 'integer',
27 | 'primary' => true,
28 | 'autocomplete' => true
29 | ),
30 | 'SETUP_LOG_ID' => array(
31 | 'data_type' => 'integer'
32 | ),
33 | 'GROUP_LABEL' => array(
34 | 'data_type' => 'string',
35 | 'required' => true,
36 | ),
37 | 'DATE' => array(
38 | 'data_type' => 'datetime',
39 | 'required' => true,
40 | ),
41 | 'SUBJECT' => array(
42 | 'data_type' => 'string',
43 | 'required' => true,
44 | ),
45 | 'UPDATE_DATA' => array(
46 | 'data_type' => 'string',
47 | 'required' => true,
48 | ),
49 | 'STATUS' => array(
50 | 'data_type' => 'integer',
51 | ),
52 | 'DESCRIPTION' => array(
53 | 'data_type' => 'string',
54 | 'required' => true,
55 | ),
56 | 'HASH' => array(
57 | 'data_type' => 'string'
58 | ),
59 | );
60 | }
61 | }
--------------------------------------------------------------------------------
/lib/entities/baseentity.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Entities;
7 |
8 |
9 | use Bitrix\Main\NotImplementedException;
10 |
11 | abstract class BaseEntity {
12 | public $id;
13 |
14 | private $isNew = true;
15 |
16 | private $_errors = array();
17 |
18 | static private $_oneRequestsCache = array();
19 | /**
20 | * @param $props
21 | * @return $this
22 | */
23 | static public function create($props) {
24 | /** @var $model BaseEntity */
25 | $model = new static;
26 | foreach ($props as $name => $value) {
27 | $model->{$name} = $value;
28 | }
29 | $model->isNew = false;
30 | return $model;
31 | }
32 |
33 | /**
34 | * @param $fields
35 | * @return $this
36 | */
37 | static private function createByRow($fields) {
38 | $props = array();
39 | $fieldsToProps = array_flip(static::map());
40 | foreach ($fields as $name => $value) {
41 | if (!isset($fieldsToProps[$name])) {
42 | continue;
43 | }
44 | $name = $fieldsToProps[$name];
45 | $props[$name] = $value;
46 | }
47 | $props = static::modifyFromDb($props);
48 | return self::create($props);
49 | }
50 |
51 | private function getRawFields() {
52 | $result = array();
53 | $data = array();
54 | foreach (static::map() as $property => $field) {
55 | $data[$property] = $this->{$property};
56 | }
57 | $data = static::modifyToDb($data);
58 | foreach (static::map() as $property => $field) {
59 | $result[$field] = $data[$property];
60 | }
61 |
62 | return $result;
63 | }
64 |
65 | /**
66 | * @param array $params
67 | * @return AppliedChangesLogModel[]
68 | */
69 | static public function find($params = array()) {
70 | $modelToDb = static::map();
71 | $fReplaceList = function ($list) use ($modelToDb) {
72 | return array_map(function ($item) use ($modelToDb) {
73 | return $modelToDb[$item];
74 | }, $list);
75 | };
76 |
77 | if ($params['select']) {
78 | $params['select'] = $fReplaceList($params['select']);
79 | }
80 | if ($params['group']) {
81 | $pGroup = array();
82 | foreach ($params['group'] as $field => $value) {
83 | $pGroup[$modelToDb[$field]] = $value;
84 | }
85 | $params['group'] = $pGroup;
86 | }
87 | if ($params['order']) {
88 | $pOrder = array();
89 | foreach ($params['order'] as $field => $value) {
90 | $pOrder[$modelToDb[$field]] = $value;
91 | }
92 | $params['order'] = $pOrder;
93 | }
94 |
95 | if ($params['filter']) {
96 | $pFilter = array();
97 | foreach ($params['filter'] as $field => $value) {
98 | $field = preg_replace_callback("/\w+/", function ($matches) use ($modelToDb) {
99 | return $modelToDb[$matches[0]];
100 | }, $field);
101 | $pFilter[$field] = $value;
102 | }
103 | $params['filter'] = $pFilter;
104 | }
105 | $dbResult = static::callGatewayMethod('getList', $params);
106 | $rows = $dbResult->fetchAll();
107 | $items = array();
108 | foreach ($rows as $row) {
109 | $items[] = self::createByRow($row);
110 | }
111 | return $items;
112 | }
113 |
114 | /**
115 | * @param array $params
116 | * @return $this
117 | */
118 | static public function findOne($params = array()) {
119 | $cacheKey = md5(get_called_class().serialize($params));
120 | if (!self::$_oneRequestsCache[$cacheKey]) {
121 | $params['limit'] = 1;
122 | $items = self::find($params);
123 | self::$_oneRequestsCache[$cacheKey] = $items[0];
124 | }
125 | return self::$_oneRequestsCache[$cacheKey];
126 | }
127 |
128 | /**
129 | * @return mixed
130 | * @internal param $p1
131 | * @internal param $p2
132 | * @internal param $p3
133 | *
134 | */
135 | static public function callGatewayMethod() {
136 | $params = func_get_args();
137 | $name = array_shift($params);
138 | return call_user_func_array(array(static::gatewayClass(), $name), $params);
139 | }
140 |
141 | public function delete() {
142 | $res = static::callGatewayMethod('delete', $this->id);
143 | return !(bool)$res->getErrors();
144 | }
145 |
146 | public function insert() {
147 | $res = static::callGatewayMethod('add', $this->getRawFields());
148 | $this->id = $res->getId();
149 | $this->_errors = $res->getErrors() ?: array();
150 | $this->isNew = false;
151 | return !(bool)$res->getErrors();
152 | }
153 |
154 | public function update() {
155 | $res = static::callGatewayMethod('update', $this->id, $this->getRawFields());
156 | $this->_errors = $res->getErrors() ?: array();
157 | return !(bool)$res->getErrors();
158 | }
159 |
160 | public function save() {
161 | return $this->isNew ? $this->insert() : $this->update();
162 | }
163 |
164 | public function getErrors() {
165 | return $this->_errors;
166 | }
167 |
168 | /**
169 | * @throws NotImplementedException
170 | * @return array
171 | */
172 | static protected function map() {
173 | throw new NotImplementedException('You should implement method `map`');
174 | }
175 |
176 | /**
177 | * @throws NotImplementedException
178 | * @return string
179 | */
180 | static protected function gatewayClass() {
181 | throw new NotImplementedException('You should implement method `gatewayClass`');
182 | }
183 |
184 | static protected function modifyFromDb($data) {
185 | return $data;
186 | }
187 |
188 | static protected function modifyToDb($data) {
189 | return $data;
190 | }
191 | }
--------------------------------------------------------------------------------
/lib/entities/setuplog.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Entities;
7 |
8 |
9 | use Bitrix\Main\Entity\DataManager;
10 |
11 | class SetupLogTable extends DataManager {
12 |
13 | public static function className() {
14 | return get_called_class();
15 | }
16 |
17 | public static function getTableName() {
18 | return 'ws_reducemigrations_setups_log';
19 | }
20 |
21 | /**
22 | * @return string
23 | */
24 | public static function getFilePath() {
25 | return __FILE__;
26 | }
27 |
28 | public static function getMap() {
29 | return array(
30 | 'ID' => array(
31 | 'data_type' => 'integer',
32 | 'primary' => true,
33 | 'autocomplete' => true
34 | ),
35 | 'DATE' => array(
36 | 'data_type' => 'datetime',
37 | 'required' => true,
38 | ),
39 | 'USER_ID' => array(
40 | 'data_type' => 'integer'
41 | )
42 | );
43 | }
44 | }
--------------------------------------------------------------------------------
/lib/entities/setuplogmodel.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Entities;
7 |
8 | use Bitrix\Main\Type\DateTime;
9 | use Bitrix\Main\UserTable;
10 | use WS\ReduceMigrations\factories\DateTimeFactory;
11 |
12 | class SetupLogModel extends BaseEntity {
13 | public
14 | $id, $userId;
15 | /**
16 | * @var \DateTime
17 | */
18 | public $date;
19 |
20 | private $userData = false;
21 |
22 | public function __construct() {
23 | $this->date = DateTimeFactory::createBase();
24 | }
25 |
26 | static protected function map() {
27 | return array(
28 | 'id' => 'ID',
29 | 'date' => 'DATE',
30 | 'userId' => 'USER_ID'
31 | );
32 | }
33 |
34 | static protected function gatewayClass() {
35 | return SetupLogTable::className();
36 | }
37 |
38 | static public function deleteById($id) {
39 | return self::callGatewayMethod('delete', $id);
40 | }
41 |
42 | static protected function modifyFromDb($data) {
43 | if ($data['date'] instanceof DateTime) {
44 | $timestamp = $data['date']->getTimestamp();
45 | $data['date'] = DateTimeFactory::createBase();
46 | $data['date']->setTimestamp($timestamp);
47 | } else {
48 | $data['date']= DateTimeFactory::createBase($data['date']);
49 | }
50 | return $data;
51 | }
52 |
53 | static protected function modifyToDb($data) {
54 | $data['date'] && $data['date'] instanceof \DateTime && $data['date'] = DateTimeFactory::createBitrix($data['date']);
55 | return $data;
56 | }
57 |
58 | /**
59 | * @return AppliedChangesLogModel[]
60 | */
61 | public function getAppliedLogs() {
62 | return AppliedChangesLogModel::find(array(
63 | 'order' => array('id' => 'desc'),
64 | 'filter' => array(
65 | '=setupLogId' => $this->id
66 | )
67 | ));
68 | }
69 |
70 | /**
71 | * @return array
72 | */
73 | private function getUserData() {
74 | if ($this->userData === false) {
75 | $this->userData = UserTable::getById($this->userId)->fetch();
76 | }
77 | return $this->userData;
78 | }
79 |
80 | public function shortUserInfo() {
81 | $res = 'cli';
82 | if ($this->userId) {
83 | $data = $this->getUserData();
84 | $res = $data['NAME'].' '.$data['LAST_NAME'];
85 | }
86 | return $res;
87 | }
88 | }
--------------------------------------------------------------------------------
/lib/exceptions/multipleequalhashexception.php:
--------------------------------------------------------------------------------
1 | format($format), $format, self::timeZone());
23 | return $object;
24 | }
25 |
26 | /**
27 | * @return \DateTimeZone
28 | */
29 | public static function timeZone() {
30 | try {
31 | $obj = new \DateTime();
32 | return $obj->getTimezone();
33 | } catch (\Exception $e) {
34 | date_default_timezone_set(self::DEFAULT_TIME_ZONE);
35 | return new \DateTimeZone(self::DEFAULT_TIME_ZONE);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/lib/localization.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class Localization {
9 |
10 | /**
11 | * @var Options
12 | */
13 | private $data;
14 |
15 | public function __construct($data) {
16 | $this->data = new Options($data);
17 | }
18 |
19 | /**
20 | * @return Options
21 | */
22 | private function getData() {
23 | return $this->data;
24 | }
25 |
26 | /**
27 | * @param string $path @see Options
28 | * @param array $replace
29 | * @return mixed
30 | */
31 | public function message($path, $replace = null) {
32 | $m = $this->getData()->get($path, '');
33 | $result = $m ?: $path;
34 | if (is_array($replace)) {
35 | $result = str_replace(array_keys($replace), array_values($replace), $m);
36 | }
37 | return $result;
38 | }
39 |
40 | /**
41 | * @param string $path @see Options
42 | * @return Localization
43 | */
44 | public function fork($path) {
45 | return new static($this->getData()->get($path));
46 | }
47 |
48 | public function getDataByPath($path) {
49 | return $this->getData()->get($path, '');
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/messageoutputinterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | final class ModuleOptions {
11 | private $moduleName = 'ws.reducemigrations';
12 |
13 | private $cache = array();
14 |
15 | /**
16 | * @staticvar self $self
17 | * @return ModuleOptions
18 | */
19 | static public function getInstance() {
20 | static $self = null;
21 | if (!$self) {
22 | $self = new self;
23 | }
24 | return $self;
25 | }
26 |
27 | private function setToDb($name, $value) {
28 | \COption::SetOptionString($this->moduleName, $name, serialize($value));
29 | }
30 |
31 | private function getFromDb($name) {
32 | $value = \COption::GetOptionString($this->moduleName, $name);
33 | return unserialize($value);
34 | }
35 |
36 | public function __set($name, $value) {
37 | $this->setToCache($name, $value);
38 | $this->setToDb($name, $value);
39 | return $value;
40 | }
41 |
42 | public function __get($name) {
43 | $value = $this->getFormCache($name);
44 | if (is_null($value)) {
45 | $value = $this->getFromDb($name);
46 | $this->setToCache($name, $value);
47 | }
48 | return $value;
49 | }
50 |
51 | /**
52 | * @param $name
53 | * @return mixed
54 | */
55 | private function getFormCache($name) {
56 | return $this->cache[$name];
57 | }
58 |
59 | /**
60 | * @param $name
61 | * @param $value
62 | */
63 | private function setToCache($name, $value) {
64 | $this->cache[$name] = $value;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/options.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class Options implements \Serializable, \ArrayAccess {
9 |
10 | private $data = array();
11 |
12 | public function __construct(array $data = null) {
13 | $data && ($this->data = $data);
14 | }
15 |
16 | /**
17 | * @param string $path
18 | * @param mixed $default
19 | *
20 | * @throws \Exception
21 | * @return mixed
22 | */
23 | public function get($path, $default = null) {
24 |
25 | $usesAliases = array();
26 | $rPath = preg_replace_callback('/\[.*?\]/', function ($matches) use (& $usesAliases) {
27 | $key = trim($matches[0], '[]');
28 | $alias = str_replace('.', '_', $key);
29 | $usesAliases[$alias] = $key;
30 |
31 | return '.' . $alias;
32 | }, $path);
33 |
34 | $arPath = explode('.', $rPath);
35 | $data = $this->data;
36 | while (($pathItem = array_shift($arPath)) !== null) {
37 | if ($usesAliases[$pathItem]) {
38 | $pathItem = $usesAliases[$pathItem];
39 | unset($usesAliases[$pathItem]);
40 | }
41 |
42 | if ($data instanceof self) {
43 | $data = $data->toArray();
44 | }
45 | if (!isset($data[$pathItem])) {
46 | if (!is_null($default)) {
47 | return $default;
48 | }
49 | throw new \Exception("Value by path `$path` not exist");
50 | }
51 | $data = $data[$pathItem];
52 | }
53 |
54 | return $data;
55 | }
56 |
57 | /**
58 | * @param string $path
59 | * @param null|mixed $default
60 | *
61 | * @throws \Exception
62 | * @return $this
63 | */
64 | public function getAsObject($path, $default = null) {
65 | $res = $this->get($path, $default);
66 | if (!is_array($res)) {
67 | throw new \Exception("Return value as object not available");
68 | }
69 |
70 | return new static($this->get($path));
71 | }
72 |
73 | /**
74 | * @param \ArrayAccess|array $mergedOptions
75 | *
76 | * @return Options
77 | */
78 | public function merge($mergedOptions) {
79 | if (is_object($mergedOptions) && $mergedOptions instanceof Options) {
80 | $mergedOptions = $mergedOptions->toArray();
81 | }
82 | foreach ($mergedOptions as $path => $value) {
83 | $this->set($path, $value);
84 | }
85 |
86 | return $this;
87 | }
88 |
89 | /**
90 | * @param string $path
91 | * @param mixed $value
92 | *
93 | * @throws \Exception
94 | * @return Options
95 | */
96 | public function set($path, $value) {
97 | $arPath = explode('.', $path);
98 | $data = &$this->data;
99 | while (($key = array_shift($arPath)) !== null) {
100 | if (empty($arPath)) {
101 | $key ? $data[$key] = $value : $data[] = $value;
102 | } else {
103 | if (!$key) {
104 | throw new \Exception('Need last iterated by path. Available: ' . $path);
105 | }
106 | if (!isset($data[$key])) {
107 | $data[$key] = array();
108 | }
109 | $data = &$data[$key];
110 | }
111 | }
112 |
113 | return $this;
114 | }
115 |
116 | public function __invoke() {
117 | $args = func_get_args();
118 | switch (count($args)) {
119 | case 1:
120 | return $this->get($args[0]);
121 | break;
122 | case 2:
123 | return $this->set($args[0], $args[1]);
124 | break;
125 | }
126 | }
127 |
128 | public function toArray() {
129 | return $this->data;
130 | }
131 |
132 | public function serialize() {
133 | return serialize($this->data);
134 | }
135 |
136 | public function unserialize($serialized) {
137 | $this->data = unserialize($serialized);
138 | }
139 |
140 | public function offsetExists($offset) {
141 | try {
142 | $this->get($offset);
143 | return true;
144 | } catch (\Exception $e) {
145 | return false;
146 | }
147 | }
148 |
149 | public function offsetGet($offset) {
150 | return $this->get($offset);
151 | }
152 |
153 | public function offsetSet($offset, $value) {
154 | $this->set($offset, $value);
155 |
156 | return $value;
157 | }
158 |
159 | public function offsetUnset($offset) {
160 | $this->set($offset, null);
161 | }
162 |
163 | public function toJson() {
164 | return json_encode($this->toArray());
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/lib/scenario/exceptions/applyscenarioexception.php:
--------------------------------------------------------------------------------
1 | setData($data);
41 | if ($printer === null) {
42 | $printer = new DumbMessageOutput();
43 | }
44 | $this->printer = $printer;
45 | }
46 |
47 | /**
48 | * @return array
49 | */
50 | public function getData() {
51 | return $this->data;
52 | }
53 |
54 | /**
55 | * @param array $value
56 | */
57 | public function setData(array $value = array()) {
58 | $this->data = $value;
59 | }
60 |
61 | /**
62 | * @param $key
63 | * @param $value
64 | */
65 | public function setDataByKey($key, $value) {
66 | $this->data[$key] = $value;
67 | }
68 |
69 | /**
70 | * @param $key
71 | *
72 | * @return mixed|null
73 | */
74 | public function getDataByKey($key) {
75 | return isset($this->data[$key]) ? $this->data[$key] : null;
76 | }
77 |
78 | /**
79 | * Check to valid class definition
80 | *
81 | * @return bool
82 | */
83 | static public function isValid() {
84 | return static::name();
85 | }
86 |
87 | static public function getShortenedHash() {
88 | return substr(static::hash(), 0 , self::SHORTENED_HASH_LENGTH);
89 | }
90 | /**
91 | * @return array
92 | */
93 | public static function getPriorities() {
94 | return array(
95 | self::PRIORITY_HIGH,
96 | self::PRIORITY_MEDIUM,
97 | self::PRIORITY_OPTIONAL,
98 | );
99 | }
100 |
101 | /**
102 | * @return bool
103 | */
104 | public function isOptional() {
105 | return self::priority() === self::PRIORITY_OPTIONAL;
106 | }
107 |
108 | /**
109 | * Runs to commit migration
110 | */
111 | abstract public function commit();
112 |
113 | /**
114 | * Runs by rollback migration
115 | */
116 | abstract public function rollback();
117 |
118 | /**
119 | * Returns name of migration
120 | *
121 | * @return string
122 | */
123 | public static function name() {
124 | return null;
125 | }
126 |
127 | /**
128 | * @return string - is hash
129 | */
130 | public static function hash() {
131 | return null;
132 | }
133 |
134 | /**
135 | * @return int approximately time in seconds
136 | */
137 | public static function approximatelyTime() {
138 | return 0;
139 | }
140 |
141 | /**
142 | * Returns priority of migration
143 | *
144 | * @return string
145 | */
146 | public static function priority() {
147 | return self::PRIORITY_HIGH;
148 | }
149 |
150 | /**
151 | * @return MessageOutputInterface
152 | */
153 | protected function printer() {
154 | return $this->printer;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/lib/tests/abstractcase.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worksolutions/bitrix-reduce-migrations/bee31077f14846bb7accf1e60445a7c0ff1debd2/lib/tests/abstractcase.php
--------------------------------------------------------------------------------
/lib/tests/cases/agentbuildercase.php:
--------------------------------------------------------------------------------
1 | localization->message('name');
13 | }
14 |
15 | public function description() {
16 | return $this->localization->message('description');
17 | }
18 |
19 | public function close() {
20 | $agent = \CAgent::GetList(null, array(
21 | 'NAME' => 'abs(0);'
22 | ))->Fetch();
23 | \CAgent::Delete($agent['ID']);
24 | }
25 |
26 |
27 | public function testAdd() {
28 | $date = new DateTime();
29 | $date->add('+1 day');
30 | $builder = new \WS\ReduceMigrations\Builder\AgentBuilder();
31 | $obAgent = $builder->addAgent('abs(0);', function (Agent $agent) use ($date) {
32 | $agent
33 | ->sort(23)
34 | ->active(true)
35 | ->nextExec($date);
36 | });
37 | $agent = \CAgent::GetList(null, array(
38 | 'ID' => $obAgent->getId()
39 | ))->Fetch();
40 |
41 | $this->assertNotEmpty($agent);
42 | $this->assertEquals($agent['NAME'], 'abs(0);');
43 | $this->assertEquals($agent['SORT'], 23);
44 | $this->assertEquals($agent['ACTIVE'], "Y");
45 | $this->assertEquals($agent['NEXT_EXEC'], $date->format('d.m.Y H:i:s'));
46 | }
47 |
48 |
49 | public function testUpdate() {
50 | $builder = new \WS\ReduceMigrations\Builder\AgentBuilder();
51 | $obAgent = $builder
52 | ->updateAgent('abs(0);', function (Agent $agent) {
53 | $agent
54 | ->active(false)
55 | ->isPeriod(true);
56 | });
57 |
58 | $agent = \CAgent::GetList(null, array(
59 | 'ID' => $obAgent->getId()
60 | ))->Fetch();
61 |
62 | $this->assertNotEmpty($agent);
63 | $this->assertEquals($agent['ACTIVE'], 'N');
64 | $this->assertEquals($agent['IS_PERIOD'], 'Y');
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/lib/tests/cases/errorexception.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Tests\Cases;
7 |
8 |
9 | class ErrorException extends \Exception
10 | {
11 |
12 | private $_dumpedValue;
13 |
14 | public function setDump($value) {
15 | $this->_dumpedValue = $value;
16 | }
17 |
18 | public function getDump() {
19 | return $this->_dumpedValue;
20 | }
21 | }
--------------------------------------------------------------------------------
/lib/tests/cases/eventsbuildercase.php:
--------------------------------------------------------------------------------
1 | localization->message('name');
15 | }
16 |
17 | public function description() {
18 | return $this->localization->message('description');
19 | }
20 |
21 | public function close() {
22 | $eventType = \CEventType::GetList(array(
23 | 'TYPE_ID' => 'WS_MIGRATION_TEST_EVENT',
24 | 'LID' => 'en'
25 | ))->Fetch();
26 | $gw = new \CEventType;
27 | $gw->Delete($eventType['ID']);
28 | }
29 |
30 | public function testAdd() {
31 | $builder = new EventsBuilder();
32 | $builder->createEventType('WS_MIGRATION_TEST_EVENT', 'ru', function (EventType $event) {
33 | $event
34 | ->name('Тестовое событие миграций')
35 | ->sort(10)
36 | ->description('#TEST# - test');
37 | $event
38 | ->addEventMessage('#EMAIL_FROM#', '#EMAIL_TO#', 's1')
39 | ->subject('Hello')
40 | ->body('Hello #TEST#!')
41 | ->bodyType(EventMessage::BODY_TYPE_HTML)
42 | ->active(true)
43 | ;
44 | $event
45 | ->addEventMessage('#FROM#', '#TO#', 's1')
46 | ->subject('Hi')
47 | ->body('Hi #TEST#!')
48 | ->active(false)
49 | ->bodyType(EventMessage::BODY_TYPE_TEXT)
50 | ;
51 | });
52 |
53 | $eventType = \CEventType::GetList(array(
54 | 'TYPE_ID' => 'WS_MIGRATION_TEST_EVENT',
55 | 'LID' => 'ru'
56 | ))->Fetch();
57 |
58 | $this->assertNotEmpty($eventType);
59 | $this->assertEquals($eventType['SORT'], 10);
60 | $this->assertNotEmpty($eventType['DESCRIPTION'], '#TEST# - test');
61 | $this->assertNotEmpty($eventType['NAME'], 'Тестовое событие миграций');
62 |
63 | $res = EventMessageTable::getList(array(
64 | 'filter' => array(
65 | 'EVENT_NAME' => 'WS_MIGRATION_TEST_EVENT'
66 | )
67 | ));
68 | $this->assertEquals($res->getSelectedRowsCount(), 2);
69 | while ($item = $res->fetch()) {
70 | if ($item['SUBJECT'] == 'Hi') {
71 | $this->assertEquals($item['BODY_TYPE'], 'text');
72 | $this->assertEquals($item['MESSAGE'], 'Hi #TEST#!');
73 | $this->assertEquals($item['LID'], 's1');
74 | $this->assertEquals($item['ACTIVE'], 'N');
75 | $this->assertEquals($item['EMAIL_FROM'], '#FROM#');
76 | $this->assertEquals($item['EMAIL_TO'], '#TO#');
77 | }
78 | }
79 | }
80 |
81 |
82 | public function testUpdate() {
83 | $builder = new EventsBuilder();
84 | $builder->updateEventType('WS_MIGRATION_TEST_EVENT', 'ru', function (EventType $type) {
85 | $type
86 | ->lid('en')
87 | ->name('Тестовое событие');
88 |
89 | foreach ($type->loadEventMessages() as $message) {
90 | if ($message->getAttribute('SUBJECT') == 'Hello') {
91 | $message->remove();
92 | }
93 | $message->bcc('#BCC#');
94 | }
95 | });
96 |
97 | $eventType = \CEventType::GetList(array(
98 | 'TYPE_ID' => 'WS_MIGRATION_TEST_EVENT',
99 | 'LID' => 'en'
100 | ))->Fetch();
101 | $this->assertTrue(!empty($eventType));
102 | $this->assertNotEmpty($eventType['NAME'], 'Тестовое событие');
103 |
104 | $res = EventMessageTable::getList(array(
105 | 'filter' => array(
106 | 'EVENT_NAME' => 'WS_MIGRATION_TEST_EVENT'
107 | )
108 | ));
109 | $this->assertEquals($res->getSelectedRowsCount(), 1);
110 | while ($item = $res->fetch()) {
111 | $this->assertEquals($item['BCC'], '#BCC#');
112 | }
113 | }
114 |
115 | }
--------------------------------------------------------------------------------
/lib/tests/cases/formbuildercase.php:
--------------------------------------------------------------------------------
1 | localization->message('name');
14 | }
15 |
16 | public function description() {
17 | return $this->localization->message('description');
18 | }
19 |
20 | public function init() {
21 | \CModule::IncludeModule('form');
22 | }
23 |
24 | public function close() {
25 | $form = \CForm::GetList($by, $order, array(
26 | 'SID' => 'TestForm',
27 | ), $isFiltered)->Fetch();
28 | if (!$form) {
29 | return;
30 | }
31 | \CForm::Delete($form['ID'], 'N');
32 | }
33 |
34 | public function testAdd() {
35 | $builder = new FormBuilder();
36 | $newForm = $builder->addForm('TestForm', 'TestForm', function (Form $form) {
37 | $form
38 | ->arSiteId(array('s1'))
39 | ->sort(10)
40 | ->description('Description')
41 | ->useCaptcha(true)
42 | ->arGroup(array(
43 | '2' => 10
44 | ))
45 | ->arMenu(array("ru" => "Анкета посетителя", "en" => "Visitor Form"))
46 | ->descriptionType('html');
47 |
48 | $form
49 | ->addField('testQuestion')
50 | ->fieldType(FormField::FIELD_TYPE_INTEGER)
51 | ->sort(33)
52 | ->active(false)
53 | ->required(true)
54 | ->title('testTitle')
55 | ->arFilterAnswerText(array("dropdown"))
56 | ->arFilterAnswerValue(array("dropdown"))
57 | ->arFilterUser(array("dropdown"))
58 | ->arFilterField(array("integer"))
59 | ->comments('test comment')
60 | ->addAnswer('Привет мир!');
61 |
62 | $form
63 | ->addField('testField')
64 | ->asField()
65 | ->title('test')
66 | ;
67 |
68 | $form
69 | ->addStatus('status')
70 | ->arGroupCanDelete(array(2))
71 | ->byDefault(true);
72 | });
73 |
74 |
75 | $form = \CForm::GetList($by, $order, array(
76 | 'ID' => $newForm->getId(),
77 | ), $isFiltered)->Fetch();
78 |
79 | $this->assertNotEmpty($form);
80 | $this->assertEquals($form['C_SORT'], 10);
81 | $this->assertNotEmpty($form['DESCRIPTION'], 'Description');
82 | $this->assertNotEmpty($form['NAME'], 'TestForm');
83 | $this->assertNotEmpty($form['USE_CAPTCHA'], 'Y');
84 |
85 | $res = \CFormField::GetList($newForm->getId(), 'ALL', $by, $order, array(), $isFiltered);
86 |
87 | $this->assertEquals($res->SelectedRowsCount(), 2);
88 | while ($item = $res->fetch()) {
89 | if ($item['SID'] == 'testQuestion') {
90 | $this->assertEquals($item['ACTIVE'], 'N');
91 | $this->assertEquals($item['ADDITIONAL'], 'N');
92 | $this->assertEquals($item['FIELD_TYPE'], 'integer');
93 | $this->assertEquals($item['TITLE'], 'testTitle');
94 | $this->assertEquals($item['C_SORT'], 33);
95 | $this->assertEquals($item['REQUIRED'], 'Y');
96 | $this->assertEquals($item['COMMENTS'], 'test comment');
97 | }
98 | if ($item['SID'] == 'testField') {
99 | $this->assertEquals($item['ADDITIONAL'], 'Y');
100 | $this->assertEquals($item['TITLE'], 'test');
101 | }
102 | }
103 |
104 | $res = \CFormStatus::GetList($newForm->getId(), $by, $order, array(), $isFiltered)->Fetch();
105 | $this->assertEquals($res['TITLE'], 'status');
106 | $this->assertEquals($res['DEFAULT_VALUE'], 'Y');
107 | }
108 |
109 |
110 | public function testUpdate() {
111 | $builder = new FormBuilder();
112 | $updatedForm = $builder->updateForm('TestForm', function (Form $form) {
113 | $form->name('MyTestForm');
114 | $field = $form
115 | ->updateField('testQuestion')
116 | ->active(true)
117 | ->required(false);
118 |
119 | $field->removeAnswer('Привет мир!');
120 |
121 | $field
122 | ->addAnswer('Test')
123 | ->value('val1');
124 |
125 | $form
126 | ->updateStatus('status')
127 | ->description('test22')
128 | ->arGroupCanDelete(array(2, 3));
129 | });
130 |
131 | $form = \CForm::GetList($by, $order, array(
132 | 'ID' => $updatedForm->getId(),
133 | ), $isFiltered)->Fetch();
134 |
135 | $this->assertNotEmpty($form);
136 | $this->assertNotEmpty($form['NAME'], 'MyTestForm');
137 |
138 | $res = \CFormField::GetList($updatedForm->getId(), 'ALL', $by, $order, array(), $isFiltered);
139 |
140 | $this->assertEquals($res->SelectedRowsCount(), 2);
141 | while ($item = $res->fetch()) {
142 | if ($item['SID'] == 'testQuestion') {
143 | $this->assertEquals($item['ACTIVE'], 'Y');
144 | $this->assertEquals($item['ADDITIONAL'], 'N');
145 | $this->assertEquals($item['FIELD_TYPE'], 'integer');
146 | $this->assertEquals($item['TITLE'], 'testTitle');
147 | $this->assertEquals($item['REQUIRED'], 'N');
148 | $this->assertEquals($item['COMMENTS'], 'test comment');
149 | }
150 | }
151 |
152 | $res = \CFormStatus::GetList($updatedForm->getId(), $by, $order, array(), $isFiltered)->Fetch();
153 | $this->assertEquals($res['DESCRIPTION'], 'test22');
154 | }
155 |
156 | }
--------------------------------------------------------------------------------
/lib/tests/cases/highloadblockbuildercase.php:
--------------------------------------------------------------------------------
1 | localization->message('name');
15 | }
16 |
17 | public function description() {
18 | return $this->localization->message('description');
19 | }
20 |
21 | public function close() {
22 | $arIblock = HighloadBlockTable::getList(array(
23 | 'filter' => array(
24 | 'TABLE_NAME' => 'test_highloadblock'
25 | )
26 | ))->fetch();
27 |
28 | HighloadBlockTable::delete($arIblock['ID']);
29 | }
30 |
31 |
32 | public function testAdd() {
33 | $builder = new HighLoadBlockBuilder();
34 | $block = $builder->addHLBlock('TestBlock', 'test_highloadblock', function (HighLoadBlock $block) {
35 | $prop = $block
36 | ->addField('uf_test1')
37 | ->sort(10)
38 | ->label(array('ru' => 'Тест'))
39 | ->type(UserField::TYPE_ENUMERATION);
40 |
41 | $prop->addEnum('Тест1');
42 | $prop->addEnum('Тест2');
43 | $prop->addEnum('Тест3');
44 |
45 | $block
46 | ->addField('uf_test2')
47 | ->label(array('ru' => 'Тест2'))
48 | ->type(UserField::TYPE_HLBLOCK);
49 |
50 | $block
51 | ->addField('uf_test3')
52 | ->label(array('ru' => 'Тест2'))
53 | ->type(UserField::TYPE_BOOLEAN);
54 |
55 | $block
56 | ->addField('uf_test4')
57 | ->label(array('ru' => 'Тест2'))
58 | ->type(UserField::TYPE_DATETIME);
59 |
60 | $block
61 | ->addField('uf_test5')
62 | ->label(array('ru' => 'Тест2'))
63 | ->type(UserField::TYPE_IBLOCK_ELEMENT);
64 |
65 | $block
66 | ->addField('uf_test6')
67 | ->label(array('ru' => 'Тест2'))
68 | ->type(UserField::TYPE_VOTE);
69 |
70 | $block
71 | ->addField('uf_test7')
72 | ->label(array('ru' => 'Тест2'))
73 | ->type(UserField::TYPE_VIDEO);
74 |
75 | $block
76 | ->addField('uf_test8')
77 | ->label(array('ru' => 'Тест2'))
78 | ->type(UserField::TYPE_IBLOCK_SECTION);
79 | });
80 |
81 |
82 | $arIblock = HighloadBlockTable::getList(array(
83 | 'filter' => array(
84 | 'ID' => $block->getId()
85 | )
86 | ))->fetch();
87 |
88 | $this->assertNotEmpty($arIblock, "hlblock wasn't created");
89 | $this->assertEquals($arIblock['TABLE_NAME'], $block->getAttribute('TABLE_NAME'));
90 | $this->assertEquals($arIblock['NAME'], $block->getAttribute('NAME'));
91 |
92 | $fields = \CUserTypeEntity::GetList(null, array(
93 | 'ENTITY_ID' => "HLBLOCK_" . $block->getId(),
94 | ));
95 |
96 | $this->assertEquals($fields->SelectedRowsCount(), 8);
97 | while ($field = $fields->Fetch()) {
98 | $field['NAME'] == 'uf_test5' && $this->assertEquals($field['USER_TYPE_ID'], UserField::TYPE_IBLOCK_ELEMENT);
99 | }
100 |
101 | }
102 |
103 | public function testUpdate() {
104 | $builder = new HighLoadBlockBuilder();
105 | $block = $builder->updateHLBlock('test_highloadblock', function (HighLoadBlock $block) {
106 | $block->name('TestBlock2');
107 | $prop = $block
108 | ->updateField('uf_test1')
109 | ->multiple(true)
110 | ->required(true);
111 | $prop->updateEnum('Тест1')->xmlId('test1');
112 | $prop->removeEnum('Тест2');
113 | });
114 | $prop = $block->updateField('uf_test1');
115 |
116 | $arIblock = HighloadBlockTable::getList(array(
117 | 'filter' => array(
118 | 'ID' => $block->getId()
119 | )
120 | ))->fetch();
121 |
122 | $this->assertEquals($arIblock['NAME'], $block->getAttribute('NAME'));
123 |
124 | $res = \CUserFieldEnum::GetList(null, array(
125 | 'USER_FIELD_ID' => $block->getId(),
126 | 'VALUE' => 'Тест2',
127 | ))->Fetch();
128 |
129 | $this->assertEmpty($res);
130 |
131 | $res = \CUserFieldEnum::GetList(null, array(
132 | 'USER_FIELD_ID' => $prop->getId(),
133 | 'VALUE' => 'Тест1',
134 | ))->Fetch();
135 |
136 | $this->assertNotEmpty($res);
137 | $this->assertEquals($res['XML_ID'], 'test1');
138 | }
139 |
140 | }
--------------------------------------------------------------------------------
/lib/tests/cases/tablebuildercase.php:
--------------------------------------------------------------------------------
1 | localization->message('name');
14 | }
15 |
16 | public function description() {
17 | return $this->localization->message('description');
18 | }
19 |
20 | public function close() {
21 |
22 | }
23 |
24 | public function testAlgorithm() {
25 | $this->add();
26 | $this->update();
27 | $this->drop();
28 | }
29 |
30 |
31 | public function add() {
32 | $tableBuilder = new TableBuilder();
33 | $tableBuilder->create('test_reducemigrations_table', function (Table $table) {
34 | $table->integer('ID')
35 | ->autoincrement(true)
36 | ->primary(true);
37 | $table->string('NAME');
38 | $table->text('ABOUT');
39 | $table->date('BIRTHDAY');
40 | });
41 |
42 | $tableInfo = $this->getTableInfo('test_reducemigrations_table');
43 |
44 | $this->assertNotEmpty($tableInfo);
45 | $this->assertEquals($tableInfo['ID']['Type'], 'int(11)');
46 | $this->assertEquals($tableInfo['ID']['Key'], 'PRI');
47 | $this->assertEquals($tableInfo['ID']['Extra'], 'auto_increment');
48 | $this->assertEquals($tableInfo['NAME']['Type'], 'varchar(255)');
49 | $this->assertEquals($tableInfo['ABOUT']['Type'], 'text');
50 | $this->assertEquals($tableInfo['BIRTHDAY']['Type'], 'date');
51 | }
52 |
53 |
54 | public function update() {
55 | $tableBuilder = new TableBuilder();
56 | $tableBuilder->addColumn('test_reducemigrations_table', 'TEST_COLUMN', 'LONGTEXT');
57 | $tableBuilder->dropColumn('test_reducemigrations_table', 'BIRTHDAY');
58 |
59 | $tableInfo = $this->getTableInfo('test_reducemigrations_table');
60 |
61 | $this->assertNotEmpty($tableInfo);
62 | $this->assertEquals($tableInfo['ID']['Type'], 'int(11)');
63 | $this->assertEquals($tableInfo['ID']['Key'], 'PRI');
64 | $this->assertEquals($tableInfo['ID']['Extra'], 'auto_increment');
65 | $this->assertEquals($tableInfo['NAME']['Type'], 'varchar(255)');
66 | $this->assertEquals($tableInfo['ABOUT']['Type'], 'text');
67 | $this->assertEquals($tableInfo['TEST_COLUMN']['Type'], 'longtext');
68 | $this->assertTrue(!isset($tableInfo['BIRTHDAY']));
69 | }
70 |
71 | public function drop() {
72 | $tableBuilder = new TableBuilder();
73 | $tableBuilder->drop('test_reducemigrations_table');
74 |
75 | try {
76 | $this->getTableInfo('test_reducemigrations_table');
77 | $this->assertTrue(false);//table wasn't deleted
78 | } catch (\Bitrix\Main\DB\SqlQueryException $e) {
79 | //everything ok
80 | }
81 | }
82 |
83 | private function getTableInfo($tableName) {
84 | $database = Application::getConnection();
85 | $res = $database->query("DESCRIBE $tableName");
86 | $info = array();
87 | while ($item = $res->fetch()) {
88 | $info[$item['Field']] = $item;
89 | }
90 |
91 | return $info;
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/lib/tests/result.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Tests;
7 |
8 |
9 | class Result {
10 | private $success;
11 | private $message;
12 | private $trace;
13 |
14 | /**
15 | * @return mixed
16 | */
17 | public function getMessage() {
18 | return $this->message;
19 | }
20 |
21 | /**
22 | * @param mixed $value
23 | * @return $this
24 | */
25 | public function setMessage($value) {
26 | $this->message = $value;
27 | return $this;
28 | }
29 |
30 | public function setTrace($aTrace) {
31 | $this->trace = $aTrace;
32 | return $this;
33 | }
34 |
35 | /**
36 | * @return mixed
37 | */
38 | public function getTrace() {
39 | return $this->trace;
40 | }
41 |
42 | /**
43 | * @return mixed
44 | */
45 | public function isSuccess() {
46 | return $this->success;
47 | }
48 |
49 | /**
50 | * @param mixed $value
51 | * @return $this
52 | */
53 | public function setSuccess($value) {
54 | $this->success = $value;
55 | return $this;
56 | }
57 |
58 | /**
59 | * @return array
60 | */
61 | public function toArray() {
62 | return array(
63 | 'STATUS' => $this->isSuccess(),
64 | 'MESSAGE' => array(
65 | 'PREVIEW' => str_replace("\n", " ", $this->getMessage()),
66 | 'DETAIL' => $this->getTrace()
67 | )
68 | );
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/lib/tests/starter.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations\Tests;
7 |
8 |
9 | use WS\ReduceMigrations\Module;
10 | use WS\ReduceMigrations\Tests\Cases\AgentBuilderCase;
11 | use WS\ReduceMigrations\Tests\Cases\EventsBuilderCase;
12 | use WS\ReduceMigrations\Tests\Cases\FormBuilderCase;
13 | use WS\ReduceMigrations\Tests\Cases\HighLoadBlockBuilderCase;
14 | use WS\ReduceMigrations\Tests\Cases\IblockBuilderCase;
15 | use WS\ReduceMigrations\Tests\Cases\TableBuilderCase;
16 |
17 | class Starter {
18 |
19 | const SECTION = 'WSREDUCEMIGRATIONS';
20 |
21 | static public function className() {
22 | return get_called_class();
23 | }
24 |
25 | /**
26 | * @return \WS\ReduceMigrations\Localization
27 | */
28 | static public function getLocalization() {
29 | return Module::getInstance()->getLocalization('tests');
30 | }
31 |
32 | static public function cases() {
33 | return array(
34 | IblockBuilderCase::className(),
35 | HighLoadBlockBuilderCase::className(),
36 | AgentBuilderCase::className(),
37 | EventsBuilderCase::className(),
38 | FormBuilderCase::className(),
39 | TableBuilderCase::className(),
40 | );
41 | }
42 |
43 | static private function getLocalizationByCase ($class) {
44 | return static::getLocalization()->fork('cases.'.$class);
45 | }
46 |
47 | /**
48 | * Run module tests
49 | * @internal param $aCheckList
50 | * @return array
51 | */
52 | static public function items() {
53 | if (!Module::getInstance()->getOptions()->useAutotests) {
54 | return array();
55 | }
56 | $points = array();
57 | $i = 1;
58 |
59 | foreach (self::cases() as $caseClass) {
60 | /** @var $case AbstractCase */
61 | $case = new $caseClass(static::getLocalizationByCase($caseClass));
62 | $points[self::SECTION.'-'.$i++] = array(
63 | 'AUTO' => 'Y',
64 | 'NAME' => $case->name(),
65 | 'DESC' => $case->description(),
66 | 'CLASS_NAME' => get_called_class(),
67 | 'METHOD_NAME' => 'run',
68 | 'PARENT' => self::SECTION,
69 | 'PARAMS' => array(
70 | 'class' => $caseClass
71 | )
72 | );
73 | }
74 |
75 | return array(
76 | 'CATEGORIES' => array(
77 | self::SECTION => array(
78 | 'NAME' => static::getLocalization()->message('run.name')
79 | )
80 | ),
81 | 'POINTS' => $points
82 | );
83 | }
84 |
85 | static public function run($params) {
86 | $class = $params['class'];
87 | $result = new Result();
88 | if (!$class) {
89 | $result->setSuccess(false);
90 | $result->setMessage('Params not is correct');
91 | return $result->toArray();
92 | }
93 | $testCase = new $class(static::getLocalizationByCase($class));
94 | if (!$testCase instanceof AbstractCase) {
95 | $result->setSuccess(false);
96 | $result->setMessage('Case class is not correct');
97 | return $result->toArray();
98 | }
99 | $refClass = new \ReflectionObject($testCase);
100 | $testMethods = array_filter($refClass->getMethods(), function (\ReflectionMethod $method) {
101 | return strpos(strtolower($method->getName()), 'test') === 0;
102 | });
103 | try {
104 | $count = 0;
105 | /** @var $method \ReflectionMethod */
106 | $testCase->init();
107 | foreach ($testMethods as $method) {
108 | $testCase->setUp();
109 | $method->invoke($testCase);
110 | $testCase->tearDown();
111 | $count++;
112 | }
113 | } catch (\Exception $e) {
114 | $result->setSuccess(false)
115 | ->setTrace($e->getTraceAsString());
116 | $message = $method->getShortName(). ', '. $e->getMessage();
117 | if ($e instanceof \WS\ReduceMigrations\Tests\Cases\ErrorException) {
118 | $e->getDump() && $message .= "\ndump: \n" . var_export($e->getDump(), true);
119 | }
120 | $result->setMessage($message);
121 | return $result->toArray();
122 | }
123 | $testCase->close();
124 | return $result->setSuccess(true)
125 | ->setMessage(static::getLocalization()->message('run.report.completed').':'.$count."\n".static::getLocalization()->message('run.report.assertions').': '.$testCase->getAssertsCount())
126 | ->toArray();
127 | }
128 | }
--------------------------------------------------------------------------------
/lib/timeformatter.php:
--------------------------------------------------------------------------------
1 | 'min', 'seconds' => 'sec']
18 | */
19 | public function __construct($lang) {
20 | $this->lang = $lang;
21 | }
22 |
23 | public function format($initialTime) {
24 | $time = round($initialTime, self::SECONDS_PRECISION);
25 | if ($time >= self::SECONDS_THRESHOLD_VALUE) {
26 | $time = round($time / self::SECONDS_IN_MINUTE, self::MINUTES_PRECISION);
27 | $time = sprintf('%s %s', $time, $this->lang['minutes']);
28 | } else {
29 | $time = sprintf('%s %s', $time, $this->lang['seconds']);
30 | }
31 | return $time;
32 | }
33 | }
--------------------------------------------------------------------------------
/lib/timer.php:
--------------------------------------------------------------------------------
1 |
4 | */
5 |
6 | namespace WS\ReduceMigrations;
7 |
8 |
9 | class Timer {
10 |
11 | private $time = 0;
12 |
13 | const TIME_PRECISION = 2;
14 |
15 | public function start() {
16 | $this->time = microtime(true);
17 | }
18 |
19 | public function stop() {
20 | $this->time = microtime(true) - $this->time;
21 | }
22 |
23 | public function getTime() {
24 | return round($this->time, self::TIME_PRECISION);
25 | }
26 |
27 | public function __toString() {
28 | return (string)$this->getTime();
29 | }
30 | }
--------------------------------------------------------------------------------
/options.php:
--------------------------------------------------------------------------------
1 | getLocalization('setup');
8 | $options = $module->getOptions();
9 |
10 | $errors = array();
11 |
12 | $fCreateDir = function ($dir) {
13 | $parts = explode('/', $dir);
14 | $dir = rtrim($_SERVER['DOCUMENT_ROOT'], '/');
15 | foreach ($parts as $part) {
16 | if (!$part) {
17 | continue;
18 | }
19 | $dir .= '/'.$part;
20 | if (!mkdir($dir)) {
21 | return false;
22 | }
23 | chmod($dir, 0777);
24 | }
25 | return true;
26 | };
27 |
28 | $fSave = function ($data) use (& $errors, $module, $options, $localization, $fCreateDir) {
29 |
30 | $catalog = $data['catalog'];
31 | $catalogError = false;
32 | if (!$catalog) {
33 | $errors[] = $localization->getDataByPath('errors.catalogFieldEmpty');
34 | $catalogError = true;
35 | }
36 | $dir = $_SERVER['DOCUMENT_ROOT'] .$catalog;
37 | if (!$catalogError && !is_dir($dir) && !$fCreateDir($catalog)) {
38 | $catalogError = true;
39 | $errors[] = $localization->getDataByPath('errors.notCreateDir');
40 | }
41 | if (!$catalogError && !is_dir($dir)) {
42 | $errors[] = $localization->getDataByPath('errors.notCreateDir');
43 | } elseif(!$catalogError) {
44 | $catalog && $options->catalogPath = $catalog;
45 | }
46 |
47 | $options->useAutotests = (bool)$data['tests'];
48 | };
49 |
50 | $_POST['data'] && $fSave($_POST['data']);
51 |
52 | $errors && CAdminMessage::ShowMessage(
53 | array(
54 | "MESSAGE" => implode(', ', $errors),
55 | "TYPE" => "ERROR"
56 | )
57 | );
58 | $form = new CAdminForm('form', array(
59 | array(
60 | 'DIV' => 't1',
61 | 'TAB' => $localization->getDataByPath('tab'),
62 | )
63 | ));
64 | echo BeginNote();
65 | echo $localization->getDataByPath('description');
66 | echo EndNote();
67 | $form->Begin(array(
68 | 'FORM_ACTION' => $APPLICATION->GetCurUri()
69 | ));
70 | $form->BeginNextFormTab();
71 | $form->AddEditField(
72 | 'data[catalog]',
73 | $localization->getDataByPath('fields.catalog'),
74 | true,
75 | array(),
76 | $options->catalogPath ?: '/bitrix/php_interface/reducemigrations'
77 | );
78 |
79 | $form->Buttons(array('btnSave' => false, 'btnApply' => true));
80 | $form->Show();
81 |
--------------------------------------------------------------------------------
/prolog.php:
--------------------------------------------------------------------------------
1 | DoInstall(array('catalog' => '/bitrix/php_interface/reducemigrations'));
14 |
15 | echo 'ok';
16 |
--------------------------------------------------------------------------------
/updater.php:
--------------------------------------------------------------------------------
1 | errorMessage[] = $mess;
13 | };
14 | //=====================================================
15 |
16 | $docRoot = rtrim($_SERVER['DOCUMENT_ROOT'], '/').'/';
17 | // install file platform version
18 | $uploadDir = $docRoot . \COption::GetOptionString("main", "upload_dir", "upload");
19 | $modulePath = rtrim($docRoot, '/').$updater->kernelPath.'/modules/'.$updater->moduleID;
20 | $updatePath = $docRoot.$updater->curModulePath;
21 |
22 | $isInstalled = \Bitrix\Main\ModuleManager::isModuleInstalled($updater->moduleID);
--------------------------------------------------------------------------------