andreyryabin/sprint.migration
Sprint Migration adds a web UI to Laravel for creating, running, and rolling back database migrations. Build migrations from existing tables, track history, and manage schema changes without leaving the browser.
Sprint Migration brings Rails/Laravel-style versioned migrations to 1C-Bitrix. Instead of clicking through the admin panel to recreate iblocks, options, user types, and agents on each environment, you commit PHP migration files and run them with a CLI command. It is the de facto standard for versioning Bitrix configuration.
Install. Either drop the module into local/modules/sprint.migration, or via Composer into local/modules:
composer require andreyryabin/sprint.migration
Then install it through Settings → Modules (or bitrix/admin/partner_modules.php) and open Settings → Sprint Migration to confirm the module is active and its options are loaded.
Configure. The module reads /local/php_interface/migrations.cfg.php by default:
<?php
return [
'migration_dir' => '/local/php_interface/migrations',
'migration_table' => 'sprint_migration_versions',
];
Create the directory, make it writable by the PHP user, and confirm:
php bitrix/modules/sprint.migration/tools/migrate.php config
php bitrix/modules/sprint.migration/tools/migrate.php list
First use case. The fastest win is capturing something you would otherwise recreate by hand. For example, to codify an iblock:
php bitrix/modules/sprint.migration/tools/migrate.php add iblock
The builder prompts for the iblock code and writes a Version20240101120000.php file containing the code that recreates it. Run migrate.php up, commit the file, and the iblock is now reproducible on any environment.
Where to look first:
migrate.php list — shows every migration and whether it is up, down, or unknown.local/php_interface/migrations/ — the actual PHP files.$this->getHelperManager() inside a migration — this is your day-to-day API surface.Migration anatomy. Each file is a timestamped class extending Sprint\Migration\Version (older docs use Migration):
<?php
namespace Sprint\Migration;
class Version20240101120000 extends Version
{
protected $description = 'Create landing iblock';
public function up()
{
$helper = $this->getHelperManager();
if (!$helper->Iblock()->getIblockIdByCode('landing')) {
$helper->Iblock()->addIblock([
'CODE' => 'landing',
'TYPE' => 'content',
'NAME' => 'Landing',
'SITE_ID' => ['s1'],
]);
}
}
public function down()
{
$this->getHelperManager()->Iblock()->deleteIblock('landing');
}
}
Ordering is numeric by timestamp prefix — not by git history — so always let the builder generate the name.
Prefer helpers over raw Bitrix API. The helper manager wraps CIBlock, CModule, CUserTypeEntity, CAgent, etc.:
| Helper | Used for |
|---|---|
Iblock() |
iblock types, iblocks, properties, elements, sections |
Hlblock() |
HL-blocks and their fields |
Option() |
module options (CModule::SetOption values) |
UserType() |
custom user fields |
Agent() |
scheduled agents |
Event() |
event types and mail templates |
Form() |
form module forms |
Sql() |
raw SQL when nothing else fits |
Option() is the workhorse for module configuration:
$helper = $this->getHelperManager();
$helper->Option()->set('main', 'CAPTCHA', 'N');
$helper->Option()->set('my.module', 'api_key', getenv('API_KEY'));
Idempotency is your responsibility. Builders emit straightforward "create" calls; you edit them to guard with existence checks (as in the sample above) so the migration is safe to re-run. This is what makes the same file work on dev, staging, and production.
Separate structure from content. Put iblock shapes, properties, and options in structure migrations. Put demo elements or seeded data in content migrations that read IDs by CODE, never hardcoded IDs. Content migrations can be skipped in production via migration_dir differences or a config flag.
Deployment and CI. The CLI stops at the first error, which is exactly what you want in a deploy script:
php bitrix/modules/sprint.migration/tools/migrate.php up
migrate.php stop halts a long migration chain mid-flight; migrate.php continue picks it up later. migrate.php cron is designed to be run from a cron entry for auto-applying releases.
Transfer between environments. migrate.php transfer moves previously run migration files from the dev directory into a "released" directory that production reads from. Use this to keep dev scratch migrations in local/ and publish only validated files to the prod migration path.
Extension points. Register custom builders in migrations.cfg.php if your team needs a project-specific generator:
'builder_class' => [\My\Builder\Custom::class],
Set migration_extend_class to a project base class if you want shared helper methods (e.g., createSeoIblock()) available on every migration. This keeps repeated boilerplate out of version files.
iblock, hlblock, and userType builders write add*() calls that fail on second execution. Always add get*IdByCode() guards or switch to the *IfNotExists variants before committing.delete. Many teams leave down() empty for destructive changes and roll forward with a new migration instead.migration_dir must be writable by the PHP user or the admin builder and transfer will silently fail. Cross-check migrate.php config against the real path, especially in Docker and CI images.sprint_migration_versions table keys on the version string. A rename makes it look like a brand-new migration (re-runs) or a missing one (reports as down). Fix behavior with a new migration, not by editing an old one.migrate.php up stops at the first error and leaves the versions after it in the down state. Because Bitrix's partial state usually prevents a clean down, the pragmatic fix is to repair the failing migration and re-run up.sprint_migration_versions will conflict. Run up from a single deploy job, or coordinate with stop/continue.migrations.cfg.php. If settings behave inconsistently between migrate.php and the admin page, check migrate.php config against the admin options panel.Migration → Version base class and command renames). Lock the package in composer.json and test upgrades in staging before rolling out.add*() methods return an ID on newer releases and a boolean on older ones. Treat return values defensively, or read back with get*IdByCode() rather than trusting the return.How can I help you explore Laravel packages today?