Migrate API і з чим його їдять. На прикладі міграції форуму з Drupal 7. Частина 1


Всього голосів: 1155
Migrate API і з чим його їдять

Я хотів би поділитись своїм досвідом міграції форуму з Drupal 7 на Drupal 8, з проблемами якими я стикався під час цього процесу і інструментами які я використовував. Також розповім за підводні каміння з якими я стикнувся при міграції форуму і термінів до нього.

Інструменти

Весь процес міграції ми будемо робити використовуючи Drush. Тепер визначимось з тим що нам необхідно для проведення міграції (я буду вказувати версії які я використовував на момент написання статті):

Drupal: 8.4.5
Модулі для міграції:
* migrate
* migrate_drupal
* migrate_plus: 8.x-4.0-beta2
* migrate_tools: 8.x-4.0-beta2

Drush: 9.1.0

Весь процес міграції відбувався з використанням:
PHP: 7.1.14
MySQL: MariaDB 10.1.31

Примітка:
Всі шляхи будуть вказані відносно кореня модуля (директорії custom_migration_forum, або так як ви назвете модуль).
Перед початком міграції вимкніть модуль rdf в Drupal 8, з ним можуть бути проблеми під час виконання Rolling back (коли скасовуємо зміни міграції).

Тестовий контент

Для актуальності інформації я вирішив по ходу написання статті дописувати модуль, який проводить міграцію форуму. Тестовий контент я створив використовуючи Devel для генерації контенту. Загальна кількість згенерованих тем форуму 300 та коментарі до них. Вийшло щось таке:
Згенерований Devel'ом контент

Підготовка до міграції

Спочатку ми розгорнемо чистий сайт на Drupal 8. На чистому сайті ми створюємо свій модуль, або використовуємо GitHub.

Створюємо файл custom_migration_forum.info.yml в який заносимо основну інформацію про наш модуль та залежності:

  1. name: Custom Migration Forum
  2. description: Custom module for migrating forum from a Drupal 7 site.
  3. package: Migrations
  4. type: module
  5. core: 8.x
  6. dependencies:
  7. - drupal:migrate
  8. - drupal:migrate_drupal
  9. - drupal:forum
  10. - migrate_plus:migrate_plus (>=4.0-beta2)
  11. - migrate_tools:migrate_tools (>=4.0-beta2)

Для того щоб видалити старі конфіги міграцій при деінсталяції модуля це потрібно описати в custom_migration_forum.install. Бувало я стикався з ситуаціями коли виникав конфлікт конфігів, через те що старі конфіги не були видалені при деінсталяції модуля, тому щоб убезпечити себе краще видаляти їх при деінсталяції модуля.
custom_migration_forum.install

  1. <?php
  2.  
  3. /**
  4.  * @file
  5.  * Contains migrate_forum_drupal8.install.
  6.  */
  7.  
  8. /**
  9.  * Implements hook_uninstall().
  10.  *
  11.  * Removes stale migration configs during uninstall.
  12.  */
  13. function custom_migration_forum_uninstall() {
  14. $query = \Drupal::database()->select('config', 'c');
  15. $query->fields('c', ['name']);
  16. $query->condition('name', $query->escapeLike('migrate_plus.') . '%', 'LIKE');
  17.  
  18. $config_names = $query->execute()->fetchAll();
  19.  
  20. // Delete each config using configFactory.
  21. foreach ($config_names as $config_name) {
  22. \Drupal::configFactory()->getEditable($config_name->name)->delete();
  23. }
  24. }

Або ви просто можете виставити залежність у кожної міграції:

  1. dependencies:
  2. enforced:
  3. module:
  4. - custom_migration_forum

Міграція термінів форуму

Оскільки форум в Drupal 7 це по суті ноди з термінами, то для початку ми повинні мігрувати терміни. Почнемо з створення плагінів для міграції термінів і словника.
src/Plugin/migrate/source/Vocabulary.php:

  1. <?php
  2.  
  3. /**
  4.  * @file
  5.  * Contains \Drupal\migrate_therasomnia\Plugin\migrate\source\Vocabulary.
  6.  */
  7.  
  8. namespace Drupal\custom_migration_forum\Plugin\migrate\source;
  9.  
  10. use Drupal\migrate\Row;
  11. use Drupal\migrate\Plugin\migrate\source\SqlBase;
  12.  
  13. /**
  14.  * Drupal 7 vocabularies source from database.
  15.  *
  16.  * @MigrateSource(
  17.  * id = "custom_migration_forum_vocabulary",
  18.  * source_provider = "taxonomy"
  19.  * )
  20.  */
  21. class Vocabulary extends SqlBase {
  22.  
  23. /**
  24.   * {@inheritdoc}
  25.   */
  26. public function query() {
  27. $query = $this->select('taxonomy_vocabulary', 'v')
  28. ->fields('v', array(
  29. 'vid',
  30. 'name',
  31. 'description',
  32. 'hierarchy',
  33. 'module',
  34. 'weight',
  35. 'machine_name'
  36. ));
  37. // Filtered out unnecessary dictionaries.
  38. $query->condition('machine_name', 'forums');
  39. return $query;
  40. }
  41.  
  42. /**
  43.   * {@inheritdoc}
  44.   */
  45. public function fields() {
  46. return array(
  47. 'vid' => $this->t('The vocabulary ID.'),
  48. 'name' => $this->t('The name of the vocabulary.'),
  49. 'description' => $this->t('The description of the vocabulary.'),
  50. 'help' => $this->t('Help text to display for the vocabulary.'),
  51. 'relations' => $this->t('Whether or not related terms are enabled within the vocabulary. (0 = disabled, 1 = enabled)'),
  52. 'hierarchy' => $this->t('The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)'),
  53. 'weight' => $this->t('The weight of the vocabulary in relation to other vocabularies.'),
  54. 'parents' => $this->t("The Drupal term IDs of the term's parents."),
  55. 'node_types' => $this->t('The names of the node types the vocabulary may be used with.'),
  56. );
  57. }
  58.  
  59. /**
  60.   * {@inheritdoc}
  61.   */
  62. public function getIds() {
  63. $ids['vid']['type'] = 'integer';
  64. return $ids;
  65. }
  66.  
  67. }

src/Plugin/migrate/source/Terms.php:

  1. <?php
  2.  
  3. /**
  4.  * @file
  5.  * Contains \Drupal\migrate_therasomnia\Plugin\migrate\source\Terms.
  6.  */
  7.  
  8. namespace Drupal\custom_migration_forum\Plugin\migrate\source;
  9.  
  10. use Drupal\migrate\Row;
  11. use Drupal\migrate\Plugin\migrate\source\SqlBase;
  12.  
  13. /**
  14.  * Drupal 7 taxonomy terms source from database.
  15.  *
  16.  * @MigrateSource(
  17.  * id = "custom_migration_forum_term",
  18.  * source_provider = "taxonomy"
  19.  * )
  20.  */
  21. class Terms extends SqlBase {
  22.  
  23. /**
  24.   * {@inheritdoc}
  25.   */
  26. public function query() {
  27. $query = $this->select('taxonomy_term_data', 'td')
  28. ->fields('td', ['tid', 'vid', 'name', 'description', 'weight', 'format'])
  29. ->fields('tv', ['vid', 'machine_name'])
  30. ->distinct();
  31. // Add table for condition on query.
  32. $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid');
  33. // Filtered out unnecessary dictionaries.
  34. $query->condition('tv.machine_name', 'forums');
  35. return $query;
  36. }
  37.  
  38. /**
  39.   * {@inheritdoc}
  40.   */
  41. public function fields() {
  42. return [
  43. 'tid' => $this->t('The term ID.'),
  44. 'vid' => $this->t('Existing term VID'),
  45. 'name' => $this->t('The name of the term.'),
  46. 'description' => $this->t('The term description.'),
  47. 'weight' => $this->t('Weight'),
  48. 'parent' => $this->t("The Drupal term IDs of the term's parents."),
  49. ];
  50. }
  51.  
  52. /**
  53.   * {@inheritdoc}
  54.   */
  55. public function prepareRow(Row $row) {
  56. // Find parents for this row.
  57. $parents = $this->select('taxonomy_term_hierarchy', 'th')
  58. ->fields('th', ['parent', 'tid']);
  59. $parents->condition('tid', $row->getSourceProperty('tid'));
  60. $parents = $parents->execute()->fetchCol();
  61. $row->setSourceProperty('parent', reset($parents));
  62. return parent::prepareRow($row);
  63. }
  64.  
  65. /**
  66.   * {@inheritdoc}
  67.   */
  68. public function getIds() {
  69. $ids['tid']['type'] = 'integer';
  70. return $ids;
  71. }
  72.  
  73. }

Після створення плаґінів для міграції термінів та словників нам треба зробити конфігурацію для нашої міграції. Для цього створюємо migrate_plus.migration.term.yml і migrate_plus.migration.vocablary.yml.
config/install/migrate_plus.migration.vocablary.yml:

  1. id: custom_migration_forum_vocabulary
  2. label: Taxonomy vocabulary forum
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: custom_migration_forum_vocabulary
  10. target: migrate
  11. process:
  12. vid:
  13. -
  14. plugin: machine_name
  15. source: machine_name
  16. -
  17. plugin: make_unique_entity_field
  18. entity_type: taxonomy_vocabulary
  19. field: vid
  20. length: 32
  21. migrated: true
  22. label: name
  23. name: name
  24. description: description
  25. hierarchy: hierarchy
  26. module: module
  27. weight: weight
  28. destination:
  29. plugin: entity:taxonomy_vocabulary

В 'source' ми вказуємо наш плаґін custom_migration_forum_vocabulary, який ми описали в класі Vocabulary.

config/install/migrate_plus.migration.term.yml:

  1. id: custom_migration_forum_term
  2. label: Taxonomy terms forum
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: custom_migration_forum_term
  10. target: migrate
  11. process:
  12. tid: tid
  13. vid:
  14. plugin: migration
  15. migration: custom_migration_forum_vocabulary
  16. source: vid
  17. name: name
  18. description: description
  19. weight: weight
  20. parent: parent
  21. changed: timestamp
  22. destination:
  23. plugin: entity:taxonomy_term
  24. migration_dependencies:
  25. required:
  26. - custom_migration_forum_vocabulary

При міграції термінів ми ставимо в vid вказуємо машинне ім'я (custom_migration_forum_vocabulary) з якої ми будемо брати ID словника форуму.

Міграція форуму

Хотів би звернути увагу що в даній статті ми мігруємо форум без додаткових полів. Перед тим як почати міграцію форумів, нам потрібно мігрувати користувачів, бо без них в форумів не буде авторів. Її ми проведемо використовуючи плаґіни з Ядра, але додамо їх в свою групу міграції "Custom Migration Forum", щоб можна було запустити всю міграцію однією командою. Нам потрібні 3 міграції:

  1. міграція ролей (custom_migration_forum_user_role)
  2. міграція користувачів (custom_migration_forum_user)
  3. міграція форматів тексту (custom_migration_forum_filter_format).

Ось наші три yml-файли.
config/install/migrate_plus.migration.user_role.yml:

  1. id: custom_migration_forum_user_role
  2. label: User roles
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: d7_user_role
  10. process:
  11. id:
  12. -
  13. plugin: machine_name
  14. source: name
  15. -
  16. plugin: user_update_8002
  17. label: name
  18. permissions:
  19. -
  20. plugin: static_map
  21. source: permissions
  22. bypass: true
  23. map:
  24. 'use PHP for block visibility': 'use PHP for settings'
  25. 'administer site-wide contact form': 'administer contact forms'
  26. 'post comments without approval': 'skip comment approval'
  27. 'edit own blog entries': 'edit own blog content'
  28. 'edit any blog entry': 'edit any blog content'
  29. 'delete own blog entries': 'delete own blog content'
  30. 'delete any blog entry': 'delete any blog content'
  31. 'create forum topics': 'create forum content'
  32. 'delete any forum topic': 'delete any forum content'
  33. 'delete own forum topics': 'delete own forum content'
  34. 'edit any forum topic': 'edit any forum content'
  35. 'edit own forum topics': 'edit own forum content'
  36. - plugin: flatten
  37. weight: weight
  38. destination:
  39. plugin: entity:user_role
  40. migration_dependencies:
  41. optional:
  42. - custom_migration_forum_filter_format

config/install/migrate_plus.migration.user.yml:

  1. id: custom_migration_forum_user
  2. label: User accounts
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. class: Drupal\user\Plugin\migrate\User
  9. source:
  10. plugin: d7_user
  11. process:
  12. uid: uid
  13. name: name
  14. pass: pass
  15. mail: mail
  16. created: created
  17. access: access
  18. login: login
  19. status: status
  20. timezone: timezone
  21. langcode:
  22. plugin: user_langcode
  23. source: language
  24. fallback_to_site_default: false
  25. preferred_langcode:
  26. plugin: user_langcode
  27. source: language
  28. fallback_to_site_default: true
  29. preferred_admin_langcode:
  30. plugin: user_langcode
  31. source: language
  32. fallback_to_site_default: true
  33. init: init
  34. roles:
  35. plugin: migration_lookup
  36. migration: custom_migration_forum_user_role
  37. source: roles
  38. user_picture:
  39. -
  40. plugin: default_value
  41. source: picture
  42. default_value: null
  43. -
  44. plugin: migration_lookup
  45. migration: d7_file
  46. destination:
  47. plugin: entity:user
  48. migration_dependencies:
  49. required:
  50. - custom_migration_forum_user_role
  51. optional:
  52. - d7_field_instance
  53. - d7_file
  54. - language
  55. - default_language
  56. - user_picture_field_instance
  57. - user_picture_entity_display
  58. - user_picture_entity_form_display

config/install/migrate_plus.migration.filter_format.yml:

  1. id: custom_migration_forum_filter_format
  2. label: Filter format configuration
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: d7_filter_format
  10. process:
  11. format: format
  12. name: name
  13. cache: cache
  14. weight: weight
  15. filters:
  16. plugin: sub_process
  17. source: filters
  18. key: '@id'
  19. process:
  20. id:
  21. plugin: filter_id
  22. bypass: true
  23. source: name
  24. map: { }
  25. settings:
  26. plugin: filter_settings
  27. source: settings
  28. status:
  29. plugin: default_value
  30. default_value: true
  31. weight: weight
  32. destination:
  33. plugin: entity:filter_format

А тепер можемо почати підготовувати плаґін для міграції матеріалів форуму.
src/Plugin/migrate/source/Forum.php:

  1. <?php
  2.  
  3. namespace Drupal\custom_migration_forum\Plugin\migrate\source;
  4.  
  5. use Drupal\Core\Extension\ModuleHandlerInterface;
  6. use Drupal\migrate\Row;
  7. use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
  8. use Drupal\Core\Database\Query\SelectInterface;
  9. use Drupal\Core\Entity\EntityManagerInterface;
  10. use Drupal\Core\Extension\ModuleHandler;
  11. use Drupal\Core\State\StateInterface;
  12. use Drupal\migrate\Plugin\MigrationInterface;
  13. use Symfony\Component\DependencyInjection\ContainerInterface;
  14.  
  15. /**
  16.  * Extract forum from Drupal 7 database.
  17.  *
  18.  * @MigrateSource(
  19.  * id = "custom_migration_forum_forum",
  20.  * )
  21.  */
  22. class Forum extends FieldableEntity {
  23.  
  24. /**
  25.   * The module handler.
  26.   *
  27.   * @var \Drupal\Core\Extension\ModuleHandlerInterface
  28.   */
  29. protected $moduleHandler;
  30.  
  31. /**
  32.   * {@inheritdoc}
  33.   */
  34. public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) {
  35. parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_manager);
  36. $this->moduleHandler = $module_handler;
  37. }
  38.  
  39. /**
  40.   * {@inheritdoc}
  41.   */
  42. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
  43. return new static(
  44. $configuration,
  45. $plugin_id,
  46. $plugin_definition,
  47. $migration,
  48. $container->get('state'),
  49. $container->get('entity.manager'),
  50. $container->get('module_handler')
  51. );
  52. }
  53.  
  54. /**
  55.   * The join options between the node and the node_revisions table.
  56.   */
  57. const JOIN = 'n.vid = nr.vid';
  58.  
  59. /**
  60.   * {@inheritdoc}
  61.   */
  62. public function query() {
  63. // Select node in its last revision.
  64. $query = $this->select('node_revision', 'nr')
  65. ->fields('n', [
  66. 'nid',
  67. 'type',
  68. 'language',
  69. 'status',
  70. 'created',
  71. 'changed',
  72. 'comment',
  73. 'promote',
  74. 'sticky',
  75. 'tnid',
  76. 'translate',
  77. ])
  78. ->fields('nr', [
  79. 'vid',
  80. 'title',
  81. 'log',
  82. 'timestamp',
  83. ])
  84. ->fields('fb', [
  85. 'body_value',
  86. 'body_format',
  87. ]);
  88.  
  89. $query->addField('n', 'uid', 'node_uid');
  90. $query->addField('n', 'type', 'node_type');
  91. $query->addField('nr', 'uid', 'revision_uid');
  92. $query->innerJoin('node', 'n', static::JOIN);
  93. $query->innerJoin('field_data_body', 'fb', 'n.nid = fb.entity_id');
  94.  
  95. // If the content_translation module is enabled, get the source langcode
  96. // to fill the content_translation_source field.
  97. if ($this->moduleHandler->moduleExists('content_translation')) {
  98. $query->leftJoin('node', 'nt', 'n.tnid = nt.nid');
  99. $query->addField('nt', 'language', 'source_langcode');
  100. }
  101.  
  102. $this->handleTranslations($query);
  103. // Filtered node type forum.
  104. $query->condition('n.type', 'forum');
  105.  
  106. return $query;
  107. }
  108.  
  109. /**
  110.   * {@inheritdoc}
  111.   */
  112. public function prepareRow(Row $row) {
  113. // Get Field API field values.
  114. foreach (array_keys($this->getFields('node', 'forum')) as $field) {
  115. $nid = $row->getSourceProperty('nid');
  116. $vid = $row->getSourceProperty('vid');
  117.  
  118. $row->setSourceProperty($field, $this->getFieldValues('node', $field, $nid, $vid));
  119. }
  120. // Make sure we always have a translation set.
  121. if ($row->getSourceProperty('tnid') == 0) {
  122. $row->setSourceProperty('tnid', $row->getSourceProperty('nid'));
  123. }
  124. return parent::prepareRow($row);
  125. }
  126.  
  127. /**
  128.   * {@inheritdoc}
  129.   */
  130. public function fields() {
  131. $fields = [
  132. 'nid' => $this->t('Node ID'),
  133. 'type' => $this->t('Type'),
  134. 'title' => $this->t('Title'),
  135. 'body_value' => $this->t('Full text of body'),
  136. 'body_format' => $this->t('Format of body'),
  137. 'node_uid' => $this->t('Node authored by (uid)'),
  138. 'revision_uid' => $this->t('Revision authored by (uid)'),
  139. 'created' => $this->t('Created timestamp'),
  140. 'changed' => $this->t('Modified timestamp'),
  141. 'status' => $this->t('Published'),
  142. 'promote' => $this->t('Promoted to front page'),
  143. 'sticky' => $this->t('Sticky at top of lists'),
  144. 'revision' => $this->t('Create new revision'),
  145. 'language' => $this->t('Language (fr, en, ...)'),
  146. 'tnid' => $this->t('The translation set id for this node'),
  147. 'timestamp' => $this->t('The timestamp the latest revision of this node was created.'),
  148. ];
  149. return $fields;
  150. }
  151.  
  152. /**
  153.   * {@inheritdoc}
  154.   */
  155. public function getIds() {
  156. $ids['nid']['type'] = 'integer';
  157. $ids['nid']['alias'] = 'n';
  158. return $ids;
  159. }
  160.  
  161. /**
  162.   * Adapt our query for translations.
  163.   *
  164.   * @param \Drupal\Core\Database\Query\SelectInterface $query
  165.   * The generated query.
  166.   */
  167. protected function handleTranslations(SelectInterface $query) {
  168. // Check whether or not we want translations.
  169. if (empty($this->configuration['translations'])) {
  170. // No translations: Yield untranslated nodes, or default translations.
  171. $query->where('n.tnid = 0 OR n.tnid = n.nid');
  172. }
  173. else {
  174. // Translations: Yield only non-default translations.
  175. $query->where('n.tnid <> 0 AND n.tnid <> n.nid');
  176. }
  177. }
  178.  
  179. }

І yml-файл до плаґіну.
config/install/migrate_plus.migration.forum.yml:

  1. id: custom_migration_forum_forum
  2. label: Custom forum migration
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: custom_migration_forum_forum
  10. node_type: forum
  11. target: migrate
  12. migration_dependencies:
  13. required:
  14. - custom_migration_forum_term
  15. - custom_migration_forum_user
  16. - custom_migration_forum_filter_format
  17. process:
  18. nid: tnid
  19. vid: vid
  20. langcode:
  21. plugin: default_value
  22. source: language
  23. default_value: 'en'
  24. title: title
  25. type:
  26. plugin: default_value
  27. default_value: forum
  28. 'body/value': body_value
  29. 'body/format': body_format
  30. uid:
  31. plugin: migration
  32. migration: custom_migration_forum_user
  33. source: node_uid
  34. status: status
  35. created: created
  36. changed: changed
  37. promote: promote
  38. sticky: sticky
  39. revision_uid: revision_uid
  40. revision_log: log
  41. revision_timestamp: timestamp
  42. taxonomy_forums:
  43. plugin: migration
  44. migration: custom_migration_forum_term
  45. source: taxonomy_forums
  46. destination:
  47. plugin: entity:node

Останнє що залишилось, це коментарі до тем, а як же без них ? :)
Створюємо плаґін і описуємо його в конфігурації.
src/Plugin/migrate/source/ForumComment.php:

  1. <?php
  2. /**
  3.  * @file
  4.  * Contains \Drupal\custom_migration_forum\Plugin\migrate\source\ForumComment.
  5.  */
  6.  
  7. namespace Drupal\custom_migration_forum\Plugin\migrate\source;
  8.  
  9. use Drupal\migrate\Row;
  10. use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
  11.  
  12. /**
  13.  * Drupal 7 comment forum source from database.
  14.  *
  15.  * @MigrateSource(
  16.  * id = "custom_migration_forum_forum_comment",
  17.  * source_provider = "comment",
  18.  * )
  19.  */
  20. class ForumComment extends FieldableEntity {
  21.  
  22. /**
  23.   * {@inheritdoc}
  24.   */
  25. public function query() {
  26. $query = $this->select('comment', 'c')->fields('c');
  27. $query->innerJoin('node', 'n', 'c.nid = n.nid');
  28. $query->addField('n', 'type', 'node_type');
  29. $query->addField('n', 'nid');
  30. $query->condition('n.type', 'forum');
  31. $query->orderBy('c.created');
  32.  
  33. return $query;
  34. }
  35.  
  36. /**
  37.   * {@inheritdoc}
  38.   */
  39. public function prepareRow(Row $row) {
  40. $cid = $row->getSourceProperty('cid');
  41.  
  42. $node_type = $row->getSourceProperty('node_type');
  43. $comment_type = 'comment_node_' . $node_type;
  44. $row->setSourceProperty('comment_type', 'comment_forum');
  45.  
  46. foreach (array_keys($this->getFields('comment', $comment_type)) as $field) {
  47. $row->setSourceProperty($field, $this->getFieldValues('comment', $field, $cid));
  48. }
  49.  
  50. return parent::prepareRow($row);
  51.  
  52. }
  53.  
  54. /**
  55.   * {@inheritdoc}
  56.   */
  57. public function fields() {
  58. return [
  59. 'cid' => $this->t('Comment ID.'),
  60. 'pid' => $this->t('Parent comment ID. If set to 0, this comment is not a reply to an existing comment.'),
  61. 'nid' => $this->t('The {node}.nid to which this comment is a reply.'),
  62. 'uid' => $this->t('The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.'),
  63. 'subject' => $this->t('The comment title.'),
  64. 'comment' => $this->t('The comment body.'),
  65. 'hostname' => $this->t("The author's host name."),
  66. 'created' => $this->t('The time that the comment was created, as a Unix timestamp.'),
  67. 'changed' => $this->t('The time that the comment was edited by its author, as a Unix timestamp.'),
  68. 'status' => $this->t('The published status of a comment. (0 = Published, 1 = Not Published)'),
  69. 'format' => $this->t('The {filter_formats}.format of the comment body.'),
  70. 'thread' => $this->t("The vancode representation of the comment's place in a thread."),
  71. 'name' => $this->t("The comment author's name. Uses {users}.name if the user is logged in, otherwise uses the value typed into the comment form."),
  72. 'mail' => $this->t("The comment author's email address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
  73. 'homepage' => $this->t("The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
  74. 'type' => $this->t("The {node}.type to which this comment is a reply."),
  75. ];
  76. }
  77.  
  78. /**
  79.   * {@inheritdoc}
  80.   */
  81. public function getIds() {
  82. $ids['cid']['type'] = 'integer';
  83. return $ids;
  84. }
  85.  
  86. }

config/install/migrate_plus.migration.forum_comment.yml:

  1. id: custom_migration_forum_forum_comment
  2. label: Comments forum
  3. migration_group: Custom Migration Forum
  4. dependencies:
  5. enforced:
  6. module:
  7. - custom_migration_forum
  8. source:
  9. plugin: custom_migration_forum_forum_comment
  10. target: migrate
  11. constants:
  12. entity_type: node
  13. process:
  14. cid: cid
  15. pid:
  16. plugin: migration_lookup
  17. migration: custom_migration_forum_forum_comment
  18. source: pid
  19. entity_id: nid
  20. entity_type: 'constants/entity_type'
  21. comment_type: comment_type
  22. field_name: comment_type
  23. subject: subject
  24. uid: uid
  25. name: name
  26. mail: mail
  27. homepage: homepage
  28. hostname: hostname
  29. created: created
  30. changed: changed
  31. status: status
  32. thread: thread
  33. comment_body: comment_body
  34. destination:
  35. plugin: entity:comment
  36. migration_dependencies:
  37. required:
  38. - custom_migration_forum_forum

Початок міграції

Тепер ми готові для проведення міграції. Додамо базу даних Drupal 7 в файл конфігурації нашого сайту на Drupal 8. Робимо це в файлі settings.php (або іншому який піключає ваші налаштування, в мене це settings.local.php):

  1. $databases['migrate']['default'] = array (
  2. 'database' => 'Drupal_7',
  3. 'username' => 'root',
  4. 'password' => 'root',
  5. 'prefix' => '',
  6. 'host' => 'localhost',
  7. 'port' => '3306',
  8. 'namespace' => 'Drupal\Core\Database\Driver\mysql',
  9. 'driver' => 'mysql',
  10. );

Вмикаємо наступні модулі:

  1. drush en migrate migrate_drupal migrate_plus migrate_tools taxonomy forum -y

або якщо ви завантажили модуль з GitHub, то можете просто увімкнути його:

  1. drush en custom_migration_forum -y

Вмикаємо модулі

Міграція через Drush

Перевіряємо список доступних міграцій:

  1. drush ms

Список міграцій

Запускаємо всі наші міграції однією командою.

  1. drush mim --group="Custom Migration Forum"

Процес міграції

Для того щоб відмінити зміни міграції існує команда drush mr:

  1. drush mr --group="Custom Migration Forum"

Відміна міграції

Також інколи виникають помилки при міграції і міграція зависає в статусі Importing або Rolling back, щоб скинути статус міграції потрібно запустити:

  1. drush php-eval 'var_dump(Drupal::keyValue("migrate_status")->set('custom_migration_forum_forum', 0))'

Де custom_migration_forum_forum це ID міграції.

Наша міграція форуму завершена і як результат ми маємо повністю мігрований форум з користувачами і коментарями до тем.

Завершена міграція на Drupal 8