Добавить пакет через composer
composer install robmorgan/phinx
Создать в корне проекта директории
mkdir db
mkdir db/migrations
mkdir db/seeds
Создать симлинк для удобства вызова команды
ln -s vendor/bin/phinx phinx
Создать в корне проекта файл phinx.php
<?php
use Bitrix\Main\Config\Configuration;
define('NOT_CHECK_PERMISSIONS', true);
define('NO_AGENT_CHECK', true);
$_SERVER['DOCUMENT_ROOT'] = realpath(__DIR__);
include $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
$connections = Configuration::getInstance()->get('connections');
$connection = $connections['default'];
// 'authorizing' as admin
$_SESSION['SESS_AUTH']['USER_ID'] = 1;
return [
'paths' => [
'migrations' => [
'\Bitrix\Main' => '%%PHINX_CONFIG_DIR%%/db/migrations',
],
'seeds' => [
'\Bitrix\Main' => '%%PHINX_CONFIG_DIR%%/db/seeds',
],
],
'environments' => [
'default_migration_table' => 'phinxlog',
'default_database' => 'production',
'production' => [
'adapter' => 'mysql',
'host' => $connection['host'],
'name' => $connection['database'],
'user' => $connection['login'],
'pass' => $connection['password'],
'port' => '3306',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
],
],
'version_order' => 'creation',
];
Основные команды
# Создаст миграцию
php phinx create MyNewMigration
# Выполнит миграцию
php phinx migrate
php phinx migrate -f
# Откатит миграцию
php phinx rollback
php phinx rollback -t 1
# Статус миграций
php phinx status
# Создаст сид
php phinx seed:create MyNewSeeder
# Запустит сиды
php phinx seed:run
php phinx seed:run -s MyNewSeeder
Примеры миграций
Подготовим класс
use <Ваш неймспейс>
/**
* Abstract Migration Class.
* It is expected that the migrations you write extend from this class.
* This abstract class proxies the various database methods to your specified
* adapter.
*/
abstract class AbstractMigration extends \Phinx\Migration\AbstractMigration
{
public function init(): void
{
define('ADMIN_SECTION', true);
include '<Ваш путь>/bitrix/modules/main/cli/bootstrap.php';
}
}
Создать типа инфоблока
use Bitrix\Main\Loader;
use <Ваш неймспейс>\AbstractMigration;
/**
* Class MyNewMigration
*/
class MyNewMigration extends AbstractMigration
{
protected const ID = 'testid';
public function up(): void
{
Loader::includeModule('iblock');
$type = new CIBlockType();
$result = $type->Add([
'ID' => self::ID,
'SECTIONS' => 'Y',
'LANG' => ['ru' => ['NAME' => 'Тестовый тип инфоблока']],
]);
if (!$result) {
throw new RuntimeException('Failed to create iblock. Error: ' . $type->LAST_ERROR);
}
}
public function down(): void
{
Loader::includeModule('iblock');
$result = CIBlockType::Delete(self::ID);
if (!$result) {
throw new RuntimeException('Failed to remove iblock');
}
}
}
Создадим инфоблок
<?php
use Bitrix\Iblock\IblockTable;
use Bitrix\Main\Loader;
use <Ваш неймспейс>\AbstractMigration;
/**
* Class CreateTestIblock
*/
class CreateTestIblock extends AbstractMigration
{
public function up(): void
{
Loader::includeModule('iblock');
$iblock = new CIBlock();
$id = $iblock->Add([
'CODE' => 'testiblock',
'API_CODE' => 'Testiblock',
'IBLOCK_TYPE_ID' => 'testid',
'NAME' => 'Тестовый iblock',
'SITE_ID' => ['s1'],
'GROUP_ID' => ['1' => 'X', '2' => 'R'],
'FIELDS' => [
'CODE' => [
'IS_REQUIRED' => 'Y',
'DEFAULT_VALUE' => [
'UNIQUE' => 'N',
'TRANSLITERATION' => 'Y',
'TRANS_LEN' => '100',
'TRANS_CASE' => 'L',
'TRANS_SPACE' => '-',
'TRANS_OTHER' => '-',
'TRANS_EAT' => 'Y',
'USE_GOOGLE' => 'N',
],
],
'SECTION_CODE' => [
'IS_REQUIRED' => 'Y',
'DEFAULT_VALUE' => [
'UNIQUE' => 'N',
'TRANSLITERATION' => 'Y',
'TRANS_LEN' => '100',
'TRANS_CASE' => 'L',
'TRANS_SPACE' => '-',
'TRANS_OTHER' => '-',
'TRANS_EAT' => 'Y',
'USE_GOOGLE' => 'N',
],
],
],
]);
if (!$id) {
throw new RuntimeException('Failed to create Blocks iblock. Error: ' . $iblock->LAST_ERROR);
}
(new CIBlockProperty())->Add([
'NAME' => 'Заголовок',
'CODE' => 'TITLE',
'PROPERTY_TYPE' => 'S',
'IBLOCK_ID' => $id,
'IS_REQUIRED' => 'Y',
]);
(new CIBlockProperty())->Add([
'NAME' => 'Изображение',
'CODE' => 'IMAGE',
'PROPERTY_TYPE' => 'F',
'IBLOCK_ID' => $id,
]);
}
public function down(): void
{
Loader::includeModule('iblock');
$iblock = IblockTable::query()
->where('API_CODE', 'Testiblock')
->fetchObject();
if ($iblock === null) {
return;
}
CIBlock::Delete($iblock->getId());
}
}
Наполним инфоблок (orm D7)
<?php
use Bitrix\Main\Application;
use Bitrix\Main\Loader;
use <Ваш неймспейс>\AbstractMigration;
use Bitrix\Iblock\Elements\ElementBonuspartnerTable;
/**
* Class FillTestiblockIblock
*/
class FillTestiblockIblock extends AbstractMigration
{
public function up(): void
{
Loader::includeModule('iblock');
self::deleteAllCurrentElements();
foreach ($this->getItems() as $item) {
$object = ElementTestiblockTable::createObject()
->setXmlId(uniqid())
->setName($item['name'])
->setSort($item['sort'])
->setTitle($item['name'])
->setImage($item['image']);
$result = $object->save();
if (!$result->isSuccess()) {
throw new Exception(current($result->getErrorMessages()));
}
}
}
public function down(): void
{
self::deleteAllCurrentElements();
}
public static function deleteAllCurrentElements()
{
if (class_exists(ElementTestiblockTable::class)) {
$elements = ElementTestiblockTable::query()
->addSelect('ID')
->fetchAll();
foreach ($elements as $element) {
CIBlockElement::Delete($element['ID']);
}
}
}
public function getItems()
{
$result = [
[
'sort' => '100',
'name' => 'Element1',
'image' => $this->saveImage('/1.png'),
],
[
'sort' => '200',
'name' => 'Element2',
'image' => $this->saveImage('/2.png'),
],
];
return $result;
}
private function saveImage(string $image): ?int
{
$path = Application::getDocumentRoot() . '/db/source' . $image; //для картинок выделим отдульную папку
$id = CFile::SaveFile(array_merge(CFile::MakeFileArray($path), ['MODULE_ID' => 'iblock']), 'iblock');
return $id ?: null;
}
}