Skip to content

Миграции

Изменения схемы — это миграции Phinx. Никаких xPDO-карт пересобирать не нужно: миграция это PHP-класс, описывающий изменение.

Два слоя, два конфига

Это самый частый источник путаницы, поэтому идёт первым:

Что меняетеМиграции лежат вКонфиг (-c)Журнал
Компонентcore/components/pageblocks/src/Database/migrationscore/components/pageblocks/src/phinx.phppb_migrations
Свой сайтcore/App/Database/migrationscore/App/phinx.phppb_app_migrations

paths в каждом конфиге указывает на каталог рядом с самим конфигом. Друг друга эти два слоя не видят. Запустили не с тем -c — ваша миграция просто не появится в status. Без ошибки, без единого слова. Выглядит ровно как файл, который не задеплоился.

bash
# слой компонента
php core/components/pageblocks/vendor/bin/phinx status \
  -c core/components/pageblocks/src/phinx.php

# слой сайта
php core/components/pageblocks/vendor/bin/phinx status -c core/App/phinx.php

Сперва план

status показывает, что ожидает применения, migrate применяет. Всегда читайте план первым и проговаривайте ожидаемый результат: миграция откатывается восстановлением дампа.

bash
phinx status  -c <конфиг>     # план
phinx migrate -c <конфиг>     # запись

Никогда не запускайте PHP от root на сервере

PHP сайта работает от пользователя сайта. Миграция, запущенная root'ом, оставляет root-owned файлы кэша, которые сайт потом не может перезаписать, — и менеджер начинает отдавать застывший лексикон. Используйте sudo -u <пользователь_сайта>.

Миграции компонента запускаются сами

При установке и обновлении пакета его резолвер migrations прогоняет слой компонента. Руками это нужно, только если файлы донесли без переустановки пакета.

Как написать

php
<?php

declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class CreatePbExampleTable extends AbstractMigration
{
    public function change(): void
    {
        $this->table('pb_example', ['id' => true, 'primary_key' => ['id']])
            ->addColumn('name', 'string', ['limit' => 100, 'null' => false, 'default' => ''])
            ->addColumn('data', 'json', ['null' => false, 'default' => '{}'])
            ->addColumn('menuindex', 'integer', ['signed' => false, 'null' => false, 'default' => 0])
            ->addColumn('published_at', 'datetime', ['null' => true, 'default' => null])
            ->addTimestamps()
            ->addColumn('deleted_at', 'datetime', ['null' => true, 'default' => null])
            ->addIndex(['name'], ['name' => 'idx_name'])
            ->create();
    }
}

Префикс таблицы подставляет Phinx — пишите pb_example, а не modx_pb_example.

Таблице, которая будет хранить данные конструктора, нужен тот же хвост, что у встроенных: data, menuindex, published_at, временные метки и deleted_at. Именно он заставляет работать публикацию, сортировку и корзину без лишнего кода.

Две ловушки, на которых уже обожглись

Индекс по колонке, которую миграция не создаёт. addIndex(['type']), где колонку переименовали в placement, проходит ревью и не мешает существующим установкам — таблица там уже есть, и Phinx миграцию не перезапускает. А на чистой базе он падает посреди прогона и оставляет компонент без половины таблиц. Create-миграции вообще исполняются только на новых установках, поэтому проверить их можно только чистой установкой.

Правка миграции, которая уже применилась. На установках, где она прошла, ничего не изменится: Phinx смотрит в журнал, а не в файл. Если исправление должно доехать до существующих баз, нужна новая миграция с проверками hasColumn() / hasIndexByName(), чтобы там, где изменение уже есть, она ничего не делала.

Удаление миграций

Миграция, у которой файла нет, а строка в журнале осталась, показывается в status как ** MISSING MIGRATION FILE **. Phinx это терпит и работает дальше.

То есть слить старые add_* в create_* можно — но только пока каждая установка их уже применила. После того как компонент уехал к чужому человеку, его база может быть в любой точке последовательности, и удалённая миграция превращается в тихо сломанную установку.

© PageBlocks 2019-present