Так уж сложилось, что в 1С-Битрикс нет никакой штатной возможности по работе с модификацией структуры БД, кроме как делать это ручками в админке.
На конференциях разработчиков, у сотрудников Битрикса постоянно просят добавить в продукт механизм миграций БД, однако нам постоянно отвечают «завтраками», что может быть, когда-нибудь мы их сделаем.
Ну а работа не стоит на месте, потребность вносить изменения в БД в автоматическом режиме с соблюдением версионности никуда не делась, и даже наоборот — зреет с каждым днем.
Надоело! Прикручиваем миграции к Битриксу сами
Как обычно, любую задачу я начинаю с изучения того, что есть в опенсорсе. Для решения проблемы миграции уже есть несколько библиотек для битрикса, которые успешно применяются на продакшене.
worksolutions/bitrix-module-migrations
На момент написания статьи данный модуль является наиболее продвинутым из тех, что мне удалось найти, с точки зрения интеграции с битриксом. Это полноценный модуль, который можно установить из marketplace. Доступны исходники на Github.
Из плюсов стоит отметить, что модуль обладает как графическим интерфейсом в админке Битрикса, так и интерфейсом командной строки. Он позволяет наглядно видеть, какие миграции, когда и кем были применены, позволяет легко откатить уже выполненные миграции также через веб-интерфейс с полным логированием всех операций. Если возможность использовать «автомиграции»: это такая штука, которая слушает обработчики событий и при изменении схемы данных позволяет записать их изменение в файл с миграцией. Также у модуля есть подробно составленная документация с описанием большинства операций, которые можно выполнять с помощью модуля. А еще у них есть удобный функционал для упрощения написания скриптов миграций, т.н. билдеры, которые реально ускоряют процесс написания своей миграции.
Но для меня этот модуль не подошел. Частично из-за тех же плюсов, которые с другой стороны являются минусами. Да, исходники доступны на гитхабе, но они в windows-1251 (очевидно, для удобства публикации модуля в marketplace). Содержать свой форк в utf-8 будет накладно.
Сами создатели модуля обязывают устанавливать модуль из маркетплейса, т.к. при установке и обновлении производятся определенные операции изменения БД.
Ну и все описанное выше делает невозможным подключение модуля через composer, а также делает невозможным его обновление при отсутствующей лицензии.
В дополнение к этому, я очень опасаюсь, что модуль миграций каким-то образом может заинтересовать администратора сайта, и он может случайно, или намеренно применить или откатить ряд миграций, что может сказаться на функциональности сайта.
Workflow работы с миграциями меня тоже не очень порадовал: версия БД почему-то хранится в файле. А еще хеши, за которыми иногда придется следить.
В связи с этим всем я отказался от использования данного модуля, хотя он почти полностью покрывает мои нужды.
Phinx
Чудесная библиотека для создания миграций. Используется во многих проектах, но не имеет никакой нативной интеграции с битриксом (оно и понятно). Обладает всем необходимым функционалом для работы с БД, конфигурируется с помощью YML или php файлов. Но для упрощения работы с ним в битриксе пришлось бы написать определенный слой, в котором будет осуществляться помощь для создания миграций.
Честно признаться, я и не особо много изучал его возможности, а их у него предостаточно. Это навело на мысль, что возможно мне пока не нужен такой гигант на проекте, пока вокруг него не будет построена инфраструктура, помогающая в более простом и удобном виде создавать миграции, специфичные под Битрикс, а времени на подробное изучение библиотеки пока нет. Но Phinx однозначно заслуживает внимания, отложим его до лучших времен.
arrilot/bitrix-migrations
Эта библиотека оказалась для меня золотой серединой. Сделанная Ильёй Некрасовым из Greensight, простая, в основе которой лежат компоненты symfony и laravel, легко подключается через composer. Работа с библиотекой строится настолько просто и очевидно, что изучение возможностей укладывается в полчаса-час. Для упрощения и ускорения написания миграций для битрикса есть функционал «шаблонов», который содержит куски предустановленного кода. Также, как и в worksolutions/bitrix-module-migrations, есть возможность автоматически следить за изменениями схемы данных некоторых сущностей и создавать файлы миграций.
В общем говоря, простота модуля, его расширяемость, отсутствие административного интерфейса и следование стандартам подкупило меня, и я решил использовать именно его.
Интегрируемся
Я не упомянул еще одного факта, который был также важен при принятии решения. В проекте, на котором мне понадобились миграции, в качестве консольного приложения используется console-jedi, и мне было важно быстро и просто интегрироваться с ним. Благо, это не составило никакого труда.
Библиотека миграций предоставляет перечень следующих команд: install, make, migrate, rollback, templates, status. Каждая команда выполнена с помощью Symfony Console Component. В console-jedi я хотел видеть эти команды в отдельном пространстве имен, чтобы не путать с командами самого console-jedi. Сказано — сделано.
сonsole-jedi позволяет встроить в себя команды из приложения двумя способами. Первый способ — встроить с помощью модуля. В корне вашего битриксового модуля должен находиться файлик cli.php, который должен возвращать массив вида
1 2 3 4 5 |
return [ 'commands' => [ //Тут массив с инициализацией ваших команд ] ]; |
О втором способе я расскажу чуть ниже.
У нас в команде как раз есть подходящий для использования первого способа модуль — maximaster/tools, в который было бы круто прикрутить миграции, и дать возможность всем разработчикам при подключении данного модуля использовать миграции на любом проекте.
Для интеграции в maximaster/tools нужно было сделать несколько простых шагов:
- Добавить зависимость maximaster/tools от arrilot/bitrix-migrations и notamedia/console-jedi
- Написать небольшой класс-адаптер, который позволит инициализировать модуль миграций и консольные команды. Тут я разделил команды на 2 вида — файловые и БДшные, ниже поясню, зачем именно. В методе addNamespaceToCommands я добавил неймспейс migrate для всех инициализированных команд.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105<?phpnamespace Maximaster;use Arrilot\BitrixMigrations\Commands\MakeCommand;use Arrilot\BitrixMigrations\Commands\InstallCommand;use Arrilot\BitrixMigrations\Commands\MigrateCommand;use Arrilot\BitrixMigrations\Commands\RollbackCommand;use Arrilot\BitrixMigrations\Commands\TemplatesCommand;use Arrilot\BitrixMigrations\Commands\StatusCommand;use Arrilot\BitrixMigrations\Migrator;use Maximaster\Extend\Arrilot\BitrixMigrations\Storages\BitrixDatabaseStorage;use Arrilot\BitrixMigrations\TemplatesCollection;/*** Класс, который позволяет прицепить команды модуля миграции к console-jedi* @package Maximaster*/class MigrationsAdapter{private $migrator = null;private $storage = null;private $templates = null;public function __construct(){$config = $this->getConfig();$this->storage = new BitrixDatabaseStorage($config[ 'table' ]);$this->templates = new TemplatesCollection();$this->templates->registerBasicTemplates();$this->migrator = new Migrator($config, $this->templates, $this->storage);}/*** Получает массив с конфигурацией модуля* table - название таблицы в БД, которая хранит миграции* dir - название директории в корне проекта, которая содержит файлы миграций* @return array*/public function getConfig(){return array('table' => 'maximaster_db_migrations','dir' => './migrations',);}/*** Получает перечень всех команд* @return \Arrilot\BitrixMigrations\Commands\AbstractCommand[]*/public function getCommands(){$migrationCommands = array_merge($this->getDatabaseCommands(), $this->getFileCommands());return $migrationCommands;}/*** Добавляет пространство имен для команд* @param \Arrilot\BitrixMigrations\Commands\AbstractCommand[] $commands* @return array*/private function addNamespaceToCommands(array $commands){foreach ($commands as &$command) {$commandName = $command->getName();$command->setName('migration:' . $commandName);}return $commands;}/*** Получает список команд, для выполнения которых требуется наличие подключения к БД* @return \Arrilot\BitrixMigrations\Commands\AbstractCommand[]*/public function getDatabaseCommands(){$config = $this->getConfig();$commands = array(new InstallCommand($config[ 'table' ], $this->storage),new MigrateCommand($this->migrator),new RollbackCommand($this->migrator),new StatusCommand($this->migrator),);return $this->addNamespaceToCommands($commands);}/*** Получает список команд, для выполнения которых НЕ требуется наличие подключения к БД* @return \Arrilot\BitrixMigrations\Commands\AbstractCommand[]*/public function getFileCommands(){$commands = array(new MakeCommand($this->migrator),new TemplatesCommand($this->templates),);return $this->addNamespaceToCommands($commands);}} - Поскольку модуль миграций рассчитывает на то, что ядро уже инициализировано полностью в момент запуска, а console-jedi работает несколько иначе, то нужно было создать небольшой враппер для класса работы с БД из модуля миграций. Единственная его цель — предоставить подключение к БД в случае, если ядро инициализировалось. Исходники можно посмотреть на гитхабе, там все тривиально.
- Подключить команды в cli.php в корне модуля:
12345678910111213<?php/*** Регистрируем все команды миграций arrilot/bitrix-migrations*/$migrationsConnector = new \Maximaster\MigrationsAdapter();$migrationsCommands = $migrationsConnector->getCommands();$config = array('commands' => $migrationsCommands);return $config;
И вуаля!
Файловые и не файловые команды
Я уже писал в более ранних постах, что мы в команде хостим и ведем разработку проектов на отдельном сервере, однако весь код хранится локально на компьютерах разработчиков и синхронизируется с сервером разработки при изменениях.
Так вот разделение команд на файловые и не файловые необходимо было для того, чтобы дать разработчикам возможность пользоваться файловыми командами миграций на своем локальном ПК даже не имея веб-сервера и битрикса. console-jedi устроен таким образом, что он будет работать и без битрикса, но команды будут доступны не все. Я решил использовать и эту возможность.
maximaster/tools — это не совсем типичный модуль в понятии Битрикса. Он не использует нативную автозагрузку битрикса, благодаря чему его классы тоже можно использовать и вне битрикса. Поэтому подключив модуль через composer, вы получаете тот же модуль миграций и адаптер к нему, которые можно использовать благодаря psr-4.
В jedi есть еще один способ добавления команд — через файл .jedi.php в корне проекта. Схема примерно та же, что и в cli.php но в нем мы зарегистрируем только файловые операции:
1 2 3 4 5 6 7 8 9 10 |
<?php use Maximaster\MigrationsAdapter; return [ 'web-dir' => 'htdocs', 'env-dir' => 'environments', 'useModules' => true, 'commands' => (new MigrationsAdapter())->getFileCommands() ]; |
после чего на локальном компе будут доступны операции миграций — создание файла и просмотр списка шаблонов миграций:
Да, джедай будет ругаться на отсутствие битрикса, но это и ожидалось. Но зато теперь на любом ПК разработчика будут доступны команды, упрощающие создание миграций. Создав файл миграции через migration:make не придется идти по ssh на сервер разработки и выкачивать нужную миграцию (особенно когда их будут сотни и тысячи)
Вот такая получилась полезная интеграция!
Я не сомневаюсь, что и для Phinx и для ws.migrations можно сделать подобную интеграцию. Коллеги из Notamedia (спасибо им за console-jedi), кстати как раз используют Phinx в своих проектах, который интегрирован с джедаем. Данная статья больше о том, каким путем я пошел для подключения миграций в Битрикс, а не о том, каким именно инструментом пользоваться для этого.
Спасибо за внимание.