Компонент "Мигратор"
Для обновление базы данных в "Юпи" используются миграционные файлы, в которых описаны необходимые действия. Есть возможность "накатывания" и "отката" миграции, соответственно методы safeUp
и safeDown
. Миграционные файлы это почти теже самые миграции, что есть в Yii, есть лишь небольшие правки, которые были внесены для корректной работы поверх поддерживаемых СУБД.
Отличия от Yii-миграций
Начнём пожалуй с того, что вместо CDbMigration
в "Юпи" используется yupe\components\DbMigration
который дополняет своим функционалом базовый класс, а именно:
- метод
getOptions
который возвращает опции создания таблицы для различных СУБД - метод
normTable
для "нормализации" именни таблицы - метод
dropTableWithForeignKeys
в котором планируется/реализовано удаление таблицы вместе с foreign-ключами
Как писать миграции для "Юпи"
Написание миграций для Юпи почти ничем не отличается от того, как это работает в Yii, достаточно лишь использовать как базовый класс yupe\components\DbMigration
и работать с методами, которые рекомендуются в нём, для совместимости со всеми поддерживаемыми СУБД. Имя файла и класса миграции формируется следующим образом - m{yymmdd_time_name}
, например 130503_091124_BlogPostImage
. В примере используется миграция, котрорая создана 03-05-2013 в 09:11:24 с именем BlogPostImage.
Миграции необходимо располагать в каталоге ./protected/modules/<module>/install/migrations/
, это делается для того, чтобы миграции относились к определённым модулям.
Рассмотрим пример миграции для "Юпи"
Ниже представлен листинг миграции для "Юпи" на примере модуля "Блог":
/**
* Blog install migration
* Класс миграций для модуля Blog:
*
* @category YupeMigration
* @package yupe
* @author YupeTeam
* @license BSD https://raw.github.com/yupe/yupe/master/LICENSE
* @link https://yupe.ru
**/
class m000000_000000_blog_base extends yupe\components\DbMigration
{
/**
* Функция настройки и создания таблицы:
*
* @return null
**/
public function safeUp()
{
// blog
$this->createTable(
'{{blog_blog}}',
array(
'id' => "pk",
'category_id' => "integer DEFAULT NULL",
'name' => "varchar(250) NOT NULL",
'description' => "text",
'icon' => "varchar(250) NOT NULL DEFAULT ''",
'slug' => "varchar(150) NOT NULL",
'lang' => "char(2) DEFAULT NULL",
'type' => "integer NOT NULL DEFAULT '1'",
'status' => "integer NOT NULL DEFAULT '1'",
'create_user_id' => "integer NOT NULL",
'update_user_id' => "integer NOT NULL",
'create_date' => "integer NOT NULL",
'update_date' => "integer NOT NULL",
),
$this->getOptions()
);
// ix
$this->createIndex("ux_{{blog_blog}}_slug_lang", '{{blog_blog}}', "slug,lang", true);
$this->createIndex("ix_{{blog_blog}}_create_user", '{{blog_blog}}', "create_user_id", false);
$this->createIndex("ix_{{blog_blog}}_update_user", '{{blog_blog}}', "update_user_id", false);
$this->createIndex("ix_{{blog_blog}}_status", '{{blog_blog}}', "status", false);
$this->createIndex("ix_{{blog_blog}}_type", '{{blog_blog}}', "type", false);
$this->createIndex("ix_{{blog_blog}}_create_date", '{{blog_blog}}', "create_date", false);
$this->createIndex("ix_{{blog_blog}}_update_date", '{{blog_blog}}', "update_date", false);
$this->createIndex("ix_{{blog_blog}}_lang", '{{blog_blog}}', "lang", false);
$this->createIndex("ix_{{blog_blog}}_slug", '{{blog_blog}}', "slug", false);
$this->createIndex("ix_{{blog_blog}}_category_id", '{{blog_blog}}', "category_id", false);
// fk
$this->addForeignKey("fk_{{blog_blog}}_create_user", '{{blog_blog}}', 'create_user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
$this->addForeignKey("fk_{{blog_blog}}_update_user", '{{blog_blog}}', 'update_user_id', '{{user_user}}', 'id', 'NO ACTION', 'NO ACTION');
$this->addForeignKey("fk_{{blog_blog}}_category_id", '{{blog_blog}}', 'category_id', '{{category_category}}', 'id', 'SET NULL', 'NO ACTION');
// post
$this->createTable(
'{{blog_post}}',
array(
'id' => "pk",
'blog_id' => "integer NOT NULL",
'create_user_id' => "integer NOT NULL",
'update_user_id' => "integer NOT NULL",
'create_date' => "integer NOT NULL",
'update_date' => "integer NOT NULL",
'publish_date' => "integer NOT NULL",
'slug' => "varchar(150) NOT NULL",
'lang' => "char(2) DEFAULT NULL",
'title' => "varchar(250) NOT NULL",
'quote' => "varchar(250) NOT NULL DEFAULT ''",
'content' => "text NOT NULL",
'link' => "varchar(250) NOT NULL DEFAULT ''",
'status' => "integer NOT NULL DEFAULT '0'",
'comment_status' => "integer NOT NULL DEFAULT '1'",
'create_user_ip' => "varchar(20) NOT NULL",
'access_type' => "integer NOT NULL DEFAULT '1'",
'keywords' => "varchar(250) NOT NULL DEFAULT ''",
'description' => "varchar(250) NOT NULL DEFAULT ''",
),
$this->getOptions()
);
//ix
$this->createIndex("ux_{{blog_post}}_lang_slug", '{{blog_post}}', "slug,lang", true);
$this->createIndex("ix_{{blog_post}}_blog_id", '{{blog_post}}', "blog_id", false);
$this->createIndex("ix_{{blog_post}}_create_user_id", '{{blog_post}}', "create_user_id", false);
$this->createIndex("ix_{{blog_post}}_update_user_id", '{{blog_post}}', "update_user_id", false);
$this->createIndex("ix_{{blog_post}}_status", '{{blog_post}}', "status", false);
$this->createIndex("ix_{{blog_post}}_access_type", '{{blog_post}}', "access_type", false);
$this->createIndex("ix_{{blog_post}}_comment_status", '{{blog_post}}', "comment_status", false);
$this->createIndex("ix_{{blog_post}}_lang", '{{blog_post}}', "lang", false);
$this->createIndex("ix_{{blog_post}}_slug", '{{blog_post}}', "slug", false);
$this->createIndex("ix_{{blog_post}}_publish_date", '{{blog_post}}', "publish_date", false);
//fks
$this->addForeignKey("fk_{{blog_post}}_blog", '{{blog_post}}', 'blog_id', '{{blog_blog}}', 'id', 'CASCADE', 'NO ACTION');
$this->addForeignKey("fk_{{blog_post}}_create_user", '{{blog_post}}', 'create_user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
$this->addForeignKey("fk_{{blog_post}}_update_user", '{{blog_post}}', 'update_user_id', '{{user_user}}', 'id', 'NO ACTION', 'NO ACTION');
// user to blog
$this->createTable(
'{{blog_user_to_blog}}',
array(
'id' => "pk",
'user_id' => "integer NOT NULL",
'blog_id' => "integer NOT NULL",
'create_date' => "integer NOT NULL",
'update_date' => "integer NOT NULL",
'role' => "integer NOT NULL DEFAULT '1'",
'status' => "integer NOT NULL DEFAULT '1'",
'note' => "varchar(250) NOT NULL DEFAULT ''",
),
$this->getOptions()
);
//ix
$this->createIndex(
"ux_{{blog_user_to_blog}}_blog_user_to_blog_u_b",
'{{blog_user_to_blog}}',
"user_id,blog_id",
true
);
$this->createIndex(
"ix_{{blog_user_to_blog}}_blog_user_to_blog_user_id",
'{{blog_user_to_blog}}',
"user_id",
false
);
$this->createIndex(
"ix_{{blog_user_to_blog}}_blog_user_to_blog_id",
'{{blog_user_to_blog}}',
"blog_id",
false
);
$this->createIndex(
"ix_{{blog_user_to_blog}}_blog_user_to_blog_status",
'{{blog_user_to_blog}}',
"status",
false
);
$this->createIndex("ix_{{blog_user_to_blog}}_blog_user_to_blog_role", '{{blog_user_to_blog}}', "role", false);
//fk
$this->addForeignKey("fk_{{blog_user_to_blog}}_blog_user_to_blog_user_id", '{{blog_user_to_blog}}', 'user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
$this->addForeignKey("fk_{{blog_user_to_blog}}_blog_user_to_blog_blog_id", '{{blog_user_to_blog}}', 'blog_id', '{{blog_blog}}', 'id', 'CASCADE', 'NO ACTION');
// tags
$this->createTable(
'{{blog_tag}}',
array(
'id' => 'pk',
'name' => 'varchar(255) NOT NULL',
),
$this->getOptions()
);
//ix
$this->createIndex("ux_{{blog_tag}}_tag_name", '{{blog_tag}}', "name", true);
// post to tag
$this->createTable(
'{{blog_post_to_tag}}',
array(
'post_id' => 'integer NOT NULL',
'tag_id' => 'integer NOT NULL',
'PRIMARY KEY (post_id, tag_id)'
),
$this->getOptions()
);
//ix
$this->createIndex("ix_{{blog_post_to_tag}}_post_id", '{{blog_post_to_tag}}', "post_id", false);
$this->createIndex("ix_{{blog_post_to_tag}}_tag_id", '{{blog_post_to_tag}}', "tag_id", false);
//fk
$this->addForeignKey("fk_{{blog_post_to_tag}}_post_id", '{{blog_post_to_tag}}', 'post_id', '{{blog_post}}', 'id', 'CASCADE', 'NO ACTION');
$this->addForeignKey("fk_{{blog_post_to_tag}}_tag_id", '{{blog_post_to_tag}}', 'tag_id', '{{blog_tag}}', 'id', 'CASCADE', 'NO ACTION');
}
/**
* Удаляем таблицы
*
* @return null
**/
public function safeDown()
{
$this->dropTableWithForeignKeys('{{blog_post_to_tag}}');
$this->dropTableWithForeignKeys('{{blog_tag}}');
$this->dropTableWithForeignKeys('{{blog_post}}');
$this->dropTableWithForeignKeys('{{blog_user_to_blog}}');
$this->dropTableWithForeignKeys('{{blog_blog}}');
}
}
Компонент "Мигратор" - предназначен для использования миграций из веб части. То есть это по сути тот же компонент, который используется в Yii, но переписан с учётом специфики работы в фронт-части.
Компонент доступен из namespace'a yupe\components\Migrator
и для своей работы использует следующие компоненты из глобального namespace'a:
- Yii;
- CDbCacheDependency;
- ErrorException;
- CException;
- CDbConnection;
- TagsCache;
- CHtml;
Методы
Метод init - Инициализируем класс
В данном методе перед инициализацией компонента проверяется наличие таблицы истории миграций, если же её нет - вызывается метод createMigrationHistoryTable()
Метод updateToLatest - Обновление модуля до актуальной миграции
В данном методе мы получаем список ещё не установленных миграций и постепенно "накатываем" их вызывая метод migrateUp
. Если же список новых миграций пуст - сообщаем об этом в лог.
@param string $module - id-модуля @return bool true
Метод checkForBadMigration - Проверяем на незавершённые миграции:
Метод проверяет список миграций на незавершённые ("плохие") миграции и возвращаем результат, есть таковые миграции у данного модуля или нет.
@param string $module - id-модуля @param string $class - класс-миграции @return bool обновились ли до миграции
Метод migrateUp - Обновляем миграцию:
Метод "накатывания" миграции до нового состяния с записью результата в журнал миграций.
@param string $module - required module @param string $class - name of migration class @return bool is updated to migration
Метод migrateDown - Даунгрейд миграции:
Метод "откатывания" миграции до старого состяния с удалением записи о данной миграции из журнала миграций.
@param string $module - required module @param string $class - name of migration class @return bool is downgraded to migration
Метод instantiateMigration - инициализация класса миграции:
Инициализируем класс миграции для последующей работы с ним.
@param string $module - required module @param string $class - class of migration @return mixed version and apply time
Метод getDbConnection - получаем коннектор к БД
@return db connection or make exception
Метод getMigrationHistory - получаем историю миграций
@param string $module - required module @param integer $limit - limit of array @return mixed version and apply time
Метод createMigrationHistoryTable - создаём таблицу миграций
@return nothing
Метод getNewMigrations - проверка на наличие новых миграций для модуля
@param string $module - required module @return mixed new migrations
Метод checkForUpdates - получение списка обновлений для выбранных модулей
@param array $modules - list of modules @return mixed new migrations
Метод getModulesWithDBInstalled - получаем список модулей с установленными миграциями
@return mixed db-installed
При возникновении проблем - напишите нам!
No Comments