1С Битрикс · 7 мин чтения

Миграции Битрикс: как переносить правки между копиями сайта

Каждый раз, когда на проекте несколько копий сайта, дев, тест, прод, встаёт один и тот же вопрос: как перенести правки в базе так, чтобы ничего не потерялось и не разъехалось. Миграции Битрикс решают эту задачу тем же способом, что и в любом другом фреймворке: изменения структуры и настроек оформляются кодом, а не делаются руками через админку. Дальше расскажу, как выстраиваю этот процесс на реальных проектах, какой инструмент использую и какие грабли уже собрал.

Зачем нужны миграции в Битрикс-проекте

В Битрикс большая часть настроек живёт не в файлах, а в базе: инфоблоки, их свойства, пользовательские поля, константы модулей, группы пользователей, агенты, обработчики событий. Разработчик открывает админку на локальной копии, добавляет свойство инфоблоку, всё работает, коммитит только код обработчика. Через неделю та же правка нужна на проде, и её приходится вспоминать и повторять руками, глядя на дев-копию как на шпаргалку.

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

Идея миграций та же, что в Laravel, Django или Rails: изменение базы описывается кодом с номером версии и двумя методами, накатить и откатить. Файл миграции лежит в git рядом с остальным кодом, применяется в одном и том же порядке на любой копии, и в базе хранится отметка, какие миграции уже выполнены. Никакой магии, просто дисциплина, оформленная в виде инструмента.

Что переносить между копиями, а что не стоит

Не всё в базе Битрикс имеет смысл гонять миграциями. Контент, который меняют редакторы, товары, цены, тексты страниц, миграциями не трогаю, это работа для обмена с 1С или ручного ввода. А вот структурные и конфигурационные вещи обязаны идти через миграции, потому что их либо мучительно объяснять текстом в таск-трекере, либо легко забыть.

Тип изменения Где хранится Что будет, если перенести руками
Инфоблоки и их свойства таблицы b_iblock, b_iblock_property разный код свойства на копиях, падают выгрузки и API-интеграции
Пользовательские поля (UF) b_user_field, b_user_field_lang поле есть в коде шаблона, но отсутствует в базе, ошибка на проде
Константы и опции модулей таблица b_option функциональность включена на тесте и выключена на проде без видимой причины
Группы пользователей и права b_group и таблицы прав доступа сотрудник видит раздел админки на одной копии и не видит на другой
Highload-блоки b_hlblock и собственная таблица блока расхождение структуры справочника между копиями

Хайлоуд-блоки выношу в миграции почти всегда, они создают собственную таблицу в базе, и просто скопировать код обработчика недостаточно, таблицу тоже нужно создать в той же структуре на каждой копии.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Инструмент для миграций: пакет для Битрикс через Composer

Голый Битрикс не поставляется с мастером миграций для пользовательского кода, поэтому на практике беру готовый пакет, arrilot/bitrix-migrations, вместо того чтобы писать велосипед под каждый проект заново. Ставится через Composer в корне проекта:

composer require arrilot/bitrix-migrations

Сам собой консольный скрипт не появляется: исполняемый файл нужно скопировать к себе командой cp vendor/arrilot/bitrix-migrations/migrator migrator, проверить внутри него правильный $_SERVER[‘DOCUMENT_ROOT’] и один раз выполнить php migrator install - эта команда создаёт в базе таблицу со списком применённых миграций. Дальше работа обычная: php migrator make название_миграции на создание файла, php migrator migrate на накат всех новых миграций, php migrator rollback на откат последней пачки.

Каждая миграция, это класс с двумя методами: up() выполняет изменение, down() его отменяет. Файлы называются с префиксом-датой, поэтому порядок применения на любой копии предсказуем и не зависит от того, кто из разработчиков когда закоммитил свою ветку.

Как выглядит миграция на практике

Пример из реальной задачи: нужно добавить пользовательское поле «Зона доставки» для разделов инфоблока с товарами, чтобы потом подставлять её в калькулятор доставки СДЭК. Миграция выглядит так:

<?php

use Arrilot\BitrixMigrations\BaseMigrations\BitrixMigration;

class AddDeliveryZoneField extends BitrixMigration
{
    private $entityId = 'IBLOCK_12_SECTION';
    private $fieldName = 'UF_DELIVERY_ZONE';

    public function up()
    {
        $userField = new \CUserTypeEntity();

        $existing = $userField->GetList([], [
            'ENTITY_ID' => $this->entityId,
            'FIELD_NAME' => $this->fieldName,
        ])->Fetch();

        if ($existing) {
            return;
        }

        $userField->Add([
            'ENTITY_ID' => $this->entityId,
            'FIELD_NAME' => $this->fieldName,
            'USER_TYPE_ID' => 'string',
            'XML_ID' => $this->fieldName,
            'SORT' => 100,
            'MULTIPLE' => 'N',
            'MANDATORY' => 'N',
            'EDIT_FORM_LABEL' => ['ru' => 'Зона доставки'],
            'LIST_COLUMN_LABEL' => ['ru' => 'Зона доставки'],
        ]);
    }

    public function down()
    {
        $userField = new \CUserTypeEntity();

        $existing = $userField->GetList([], [
            'ENTITY_ID' => $this->entityId,
            'FIELD_NAME' => $this->fieldName,
        ])->Fetch();

        if ($existing) {
            $userField->Delete($existing['ID']);
        }
    }
}

Проверка на существующее поле в начале up() критична, без неё повторный запуск миграции, например после случайного повторного деплоя, упадёт с ошибкой дублирования или создаст второе похожее поле. Такая проверка занимает три строки, а спасает от разбора кривых данных в проде.

Порядок переноса правок между копиями

У меня процесс обычно такой:

  1. Разработчик пишет миграцию локально и сразу проверяет откат: накатил, убедился, что всё работает, откатил, убедился, что база вернулась в исходное состояние.
  2. Миграция коммитится в ветку вместе с кодом, который её использует, в одном пул-реквесте, а не отдельным коммитом позже.
  3. На тестовой копии деплой-скрипт после git pull запускает php migrator migrate автоматически, без участия человека.
  4. Заказчик или тестировщик проверяет функциональность на тестовой копии.
  5. При деплое на прод выполняется та же команда в том же деплой-скрипте, что и на тесте, без исключений «на проде я быстренько сам через админку».

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

Перед деплоем на прод с рискованными миграциями, удаление поля, массовое изменение данных, обязательно снимаю бэкап базы. Откат down() спасает от логической ошибки в самой миграции, но не восстановит данные, которые она удалила в процессе up().

Типичные проблемы при переносе миграций между копиями

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

Забытый файл миграции при переносе кода без контроля версий. Встречается на старых проектах, где часть правок ещё гоняют через FTP или прямой доступ к серверу. Как только в процессе появляется миграция, весь перенос кода обязан идти через git, иначе таблица применённых миграций начинает врать.

Тяжёлые миграции, которые блокируют таблицу на проде. На большой базе ALTER TABLE или массовый UPDATE может занять не секунды, а минуты, в течение которых сайт частично недоступен, по моей практике на таблице с несколькими сотнями тысяч строк такая операция иногда растягивалась на пару минут. Такие миграции проверяю на копии с объёмом данных, близким к продакшену, до переноса на прод, а не после.

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

1С-Битрикс регулярно обновляется, поэтому перед внедрением я сверяюсь с актуальной документацией.

Частые вопросы

Чем миграции в Битрикс отличаются от миграций в Laravel или Django?

Логика та же: класс с up() и down(), таблица с отметками о применённых версиях, порядок по дате в имени файла. Разница в API, который используется внутри миграции, вместо ORM фреймворка вызываются классы ядра Битрикс: CIBlock, CUserTypeEntity, COption и подобные. Готового мастера миграций в коробке 1С-Битрикс: Управление сайтом нет, поэтому подключаю сторонний пакет через Composer.

Можно ли просто копировать базу целиком вместо миграций?

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

Что делать, если прод и тест уже разошлись без миграций?

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

Нужно ли переносить миграциями изменения торгового каталога, цены и скидки?

Сами цены и остатки нет, это данные, которые меняются постоянно и приходят через обмен с 1С или руками менеджера. А вот типы цен, правила скидок, свойства торгового предложения переношу миграциями наравне с инфоблоками, это структура каталога, а не его наполнение.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно