Импорт данных с фреймворком migrate. Владислав Богатырев
TRANSCRIPT
Импорт данных с фреймворком Migrate
Владислав Богатырев
http://donetsk.drupal.ua
Импорт данных с фреймворком Migrate
Владислав Богатырев
Ноябрь 2011
Задача доклада
• С какими проблемами я столкнусь при миграции данных на Drupal7?
• Что из это может решить Migrate?• Использовать ли Migrate ?
Структура доклада
• Задача, что мигрируем.• Проблемы• Выбор платформы.• Что такое Migrate.• Пример кода миграции.• Миграция множественных значений и сложных полей.• Сохранение связей при изменении ИД элементов. • Что еще может Migrate
Задача
Из некоторой структуры данных, (обычно это CMS на основе БД), скопировать в Drupal набор объектов.
Обычный набор, это :• ноды с их набором артибутов• термины таксономии• пользователи• комментарии• файлы• другие данные, как например оценки контента
пользователями• связи между всем перечисленным
Это значит, что ...
Проблемы
- вам нужно изучить внутреннюю структуру старой БД(скорее всего данные будут разбросаны по разным таблицам) - вам нужно знать API Drupal и выполнять импорт через него.- почти наверняка потребуется знание структуры хранения данных в Drupal- вам нужно написать код по копированию данных- вам нужно знать как изменились ИД объектов для сохранение связей- возможно, вы захотите использовать Batch API
РешениеЭто же Drupal! Давайте используем модуль для этого!
• Feeds http://drupal.org/project/feeds • Десятки плагинов для Feeds -) • Node Export (dev) http://drupal.org/project/node_export• Wordpress Migrate
http://drupal.org/project/wordpress_migrate• Joomla to Drupal http://drupal.org/project/joomla• phpBB2Drupal (dev)
http://drupal.org/project/phpbb2drupal • Blogger Importer
http://drupal.org/project/blogger_importer • TYPO3_migrate http://drupal.org/project/TYPO3_migrate• Migrate http://drupal.org/project/migrate
Migrate (7.x-2.2)
- вся работа происходит в коде, это фреймворк- любой источник данных*- любая entity назначения*- работает с любыми полями*- умеет переводить старые ИД в новые - хорошая документация- ООП - много дополнительных классов на DO- Кое в чем может помочь migrate_extras * но иногда для этого придется определить свой класс :)
Как выглядит MigrateНемного вот так:
Как выглядит MigrateНо в основном так:
Начало работы
1. Устанавливаем модули: migrate, migrate_ui 2. Создаем свой модуль:
3. Добавляем .inc файлы где будет располагаться код для миграции.
1. /*2. * You must implement hook_migrate_api(), setting the API level to 2, for3. * your migration classes to be recognized by the Migrate module.4. */5. function migrate_example_migrate_api() {6. $api = array(7. 'api' => 2,8. );9. return $api;10.}11.
Пример. Миграция нод.
1. abstract class BasicExampleMigration extends Migration {2. public function __construct() {3. 4. parent::__construct();5. 6. $this->team = array(7. new MigrateTeamMember('Liz Taster', '[email protected]', t('Product Owner')),8. new MigrateTeamMember('Larry Brewer', '[email protected]', t('Implementor')),9. );10. 11. $this->issuePattern = 'http://drupal.org/node/:id:';12. }13.}14.
Определяем базовый класс с общими атрибутами и методами.
Пример. Миграция нод.
1. class BeerNodeMigration extends BasicExampleMigration {2. public function __construct() {3. parent::__construct();4. $this->description = t('Beers of the world');5. $this->dependencies = array('BeerTerm', 'BeerUser');6. 7. // карта старых и новых ИД мигрируемых объектов8. $this->map = new MigrateSQLMap($this->machineName,9. array(10. 'bid' => array(11. 'type' => 'int',12. 'not null' => TRUE,13. 'description' => 'Beer ID.',14. 'alias' => 'b',15. )16. ),17. MigrateDestinationNode::getKeySchema()18. );19. 20.
Класс миграции нод. (Начало)
1. 2. // объект выборки данных из источника3. $query = db_select('migrate_example_beer_node', 'b')4. ->fields('b', array('bid', 'name', ...));5. $query->...6. 7. // определяем атрибут класса - источник данных8. $this->source = new MigrateSourceSQL($query);9. 10. // определяем атрибут класса - объект назначения данных11. $this->destination = new MigrateDestinationNode('migrate_example_beer');12. 13. // связываем поля источника и назначения14. $this->addFieldMapping('title', 'name')15. ->description(t('Mapping beer name in source to node title'));16. 17. // другие простые поля определяются аналогично18. ...19. 20. }21. }22.
Пример. Миграция нод.Класс миграции нод. (конец)
1. class BeerUserMigration extends BasicExampleMigration {2. public function __construct() {3. parent::__construct();4. $this->description = t('Beer Drinkers of the world');5. $this->map = new MigrateSQLMap($this->machineName,6. array('aid' => array(7. 'type' => 'int',8. 'not null' => TRUE,9. 'description' => 'Account ID.'10. )11. ),12. MigrateDestinationUser::getKeySchema()13. );14. 15. // объект выборки данных из источника16. $query = db_select('migrate_example_beer_account', 'mea')17. ->fields('mea', array('aid', 'status', 'posted', 'name', 'nickname', ...));18. 19. // определяем атрибут класса - источник данных20. $this->source = new MigrateSourceSQL($query);21. 22. // определяем атрибут класса - объект назначения данных23. $this->destination = new MigrateDestinationUser();24. 25. // связываем поля источника и назначения26. $this->addFieldMapping('created', 'posted');27. 28. ...29. }30.}31.
Пример. Миграция пользователей.
Ключевые моменты. Карта ИД.
1. $this->map = new MigrateSQLMap($this->machineName,2. array('aid' => array(3. 'type' => 'int',4. 'not null' => TRUE,5. 'description' => 'Account ID.' 6. )7. ),8. MigrateDestinationUser::getKeySchema()9. );10.
Ключевые моменты. Источник и назначение.
1. // определяем атрибут класса - источник данных2. $this->source = new MigrateSourceSQL($query);3. 4. // определяем атрибут класса - объект назначения данных5. $this->destination = new MigrateDestinationUser();6.
1. // связываем поля источника и назначения2. $this->addFieldMapping('created', 'posted');3.
Ключевые моменты. Связывание полей.
• Маппинг сложных полей• Маппинг полей с множественными значениями• Значения по умолчанию• Обработка поля источника и поля назначения
перед сохранением. o Дополнительные обращение к БД o Работа с объектом новой entity
1.$query = db_select('migrate_example_beer_node', 'b')2. 3. ->fields('b', array('bid', 'name', ... ));4. 5.$query->leftJoin('migrate_example_beer_topic_node',6. 7. 'tb', 'b.bid = tb.bid');8. 9.$query->groupBy('tb.bid');10. 11.$query->addExpression('GROUP_CONCAT(tb.style)', 'terms');12. 13. 14. 15.$this->addFieldMapping('migrate_example_beer_styles', 'terms')16. 17. ->separator(',');18. 19.
Маппинг полей с множественными значениями на примере таксономии.
1. public function prepareRow($current_row) {2. 3. parent::prepareRow($current_row);4. 5. // Добавляем разделенные запятой термины к объекту рядя источника6. // с помощью кастомного метода7. $current_row->terms = $this->get_node_term_names(8. $current_row->nid,9. array(CRNAAccreditationTermMigration::$source_vocabulary_vid)10. );11. 12. return TRUE;13. }14.}15.
Маппинг полей с множественными значениями на примере таксономии.
1. $this->addFieldMapping('migrate_example_beer_styles', 'terms')2. ->separator(',');
prepare(stdClass $account, stdClass $row)
Маппинг полей.
prepare(stdClass $entity, stdClass $row)
Перевод ИД.
1. // объявляем зависимость2. $this->dependencies = array('BeerTerm', 'BeerUser');3. 4. // при маппинге поля просим фреймврок заменять значение 'aid' на новое,5. // из таблицы карты миграции и устанавливаем значение по умолчанию6. $this->addFieldMapping('uid', 'aid')7. ->sourceMigration('BeerUser')8. ->defaultValue(1);9.
В конструкторе класса миграции BeerNodeMigration:
Перевод ИД.
1.$this->addFieldMapping('pid', 'cid_parent')2. 3. ->sourceMigration('BeerComment')4. 5. ->description('Parent comment.');6. 7.
В конструкторе класса миграции BeerCommentMigration:
Миграция сложных полей на примере addressfield.
Для миграции сложного поля, необходимо:• проверить существование соответвующего класса• создать класс в случае необходимости• использовать соответвующий синтаксис маппинга
ИЛИ
Использовать метод prepare(stdClass $entity, stdClass $row)
Миграция сложных полей на примере addressfield.
1. $arguments = array(2. 'administrative_area' => array('source_field' => 'province'),3. 'locality' => array('source_field' => 'city'),4. 'thoroughfare' => array('source_field' => 'street'),5. 'premise' => array('source_field' => 'additional'),6. 'postal_code' => array('source_field' => 'postal_code'),7. );8. $this->addFieldMapping(‘field_address’, ‘country’)9. ->arguments($arguments)10. ->description(t('Mapping field_address'));11.
Маппинг поля в конструкторе.
Кастомный класс для маппинга addrressfield >>>
Миграция сложных полей на примере addressfield.
1. class MigrateAddressFieldHandler extends MigrateFieldHandler {2. public function __construct() {3. $this->registerTypes(array('addressfield'));4. }5. 6. public function prepare($entity, array $field_info, array $instance, array $values)
{7. $arguments = array();8. if (isset($values['arguments'])) {9. $arguments = array_filter($values['arguments']);10. unset($values['arguments']);11. }12. $language = $this->getFieldLanguage($entity, $field_info, $arguments);13. 14. // Setup the standard Field API array for saving.15. $delta = 0;16. foreach ($values as $value) {17. $return[$language]
[$delta] = array('country' => $value) + array_intersect_key($arguments,$field_info['columns']);
18. $delta++;19. }20. 21. return isset($return) ? $return : NULL;22. }23.}24.
Что еще может Migrate
• Работа с файлами• Поддержка XML, JSON, CSV, • Поддержка с различных типов БД источника (MSSQL,
Oracle)• Миграция alias'ов• Интеграция с Drush• Откат импорта • Интерфейс для контроля прогресса и маппинга • and even more...
Импорт данных с фреймворком Migrate
Владислав Богатырев
Ноябрь 2011
It is powerful.Use it!