APIとその使用対象を移行します。 例としてDrupal 7フォーラム移行を使用するパート1

Migrate API     .      Drupal 7.  1

このガイドは記事の翻訳です。


フォーラムをDrupal 7からDrupal 8に移行した経験を共有し、このプロセス中に遭遇した問題や、使用したツールについてお話ししたいと思います。 さらに、フォーラムの移行中に遭遇した落とし穴とその条件についても説明します。



ツール



Drushを使用して移行プロセス全体を実行します。 次に、 移行に必要なものを正確に決定しましょう(執筆時点で使用されるバージョンを示します)。



Drupal: 8.4.5



移行のためのモジュール:





ブラシ:9.1.0



移行プロセス全体は、



PHP: 7.1.14

MySQL:MariaDB 10.1.31

注:



  • すべてのパスは、モジュールのルート(custom_migration_forumディレクトリ、またはモジュールに付けた名前)を基準にして示されます。
  • 移行を開始する前に、Drupal 8のrdfモジュールを無効にします。これは、実行のロールバック中に問題を引き起こす可能性があるためです(移行の変更をキャンセルする場合)。


テスト内容



情報の関連性については、記事の執筆中に、フォーラムの移行を行うモジュールを追加することにしました。 Develを使用してコンテンツを生成するテストコンテンツを作成しました。 生成されたフォーラムトピックの総数は300個で、それらに関するコメントもあります。 これが判明しました:



 Devel'



移行の準備



最初に、Drupal 8にクリーンサイトをデプロイします。クリーンサイトでは、独自のモジュールを作成するか、 GitHubを使用します



custom_migration_forum.info.ymlファイルを作成します。 このファイルには、モジュールと依存関係に関する基本情報を入力します。



name: Custom Migration Forum description: Custom module for migrating forum from a Drupal 7 site. package: Migrations type: module core: 8.x dependencies: - drupal:migrate - drupal:migrate_drupal - drupal:forum - migrate_plus:migrate_plus (>=4.0-beta2) - migrate_tools:migrate_tools (>=4.0-beta2)
      
      





モジュールをアンインストールするときに古い移行構成を削除するには、これをcustom_migration_forum.installに記述する必要があります。 モジュールのアンインストール時に古い構成が削除されなかったために、構成の競合が発生することがありました。 したがって、自分自身を保護するために、モジュールをアンインストールするときに削除することをお勧めします。



custom_migration_forum.install



 <?php /** * @file * Contains migrate_forum_drupal8.install. */ /** * Implements hook_uninstall(). * * Removes stale migration configs during uninstall. */ function custom_migration_forum_uninstall() { $query = \Drupal::database()->select('config', 'c'); $query->fields('c', ['name']); $query->condition('name', $query->escapeLike('migrate_plus.') . '%', 'LIKE'); $config_names = $query->execute()->fetchAll(); // Delete each config using configFactory. foreach ($config_names as $config_name) { \Drupal::configFactory()->getEditable($config_name->name)->delete(); } }
      
      





または、移行ごとに依存関係を登録する必要があります。

 dependencies: enforced: module: - custom_migration_forum
      
      





フォーラム期間の移行



Drupal 7のフォーラムは基本的に用語のあるノードであるため、まずは用語を移行する必要があります。 用語と辞書を移行するためのプラグインを作成することから始めましょう。



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



 <?php /** * @file * Contains \Drupal\migrate_therasomnia\Plugin\migrate\source\Vocabulary. */ namespace Drupal\custom_migration_forum\Plugin\migrate\source; use Drupal\migrate\Row; use Drupal\migrate\Plugin\migrate\source\SqlBase; /** * Drupal 7 vocabularies source from database. * * @MigrateSource( * id = "custom_migration_forum_vocabulary", * source_provider = "taxonomy" * ) */ class Vocabulary extends SqlBase { /** * {@inheritdoc} */ public function query() { $query = $this->select('taxonomy_vocabulary', 'v') ->fields('v', array( 'vid', 'name', 'description', 'hierarchy', 'module', 'weight', 'machine_name' )); // Filtered out unnecessary dictionaries. $query->condition('machine_name', 'forums'); return $query; } /** * {@inheritdoc} */ public function fields() { return array( 'vid' => $this->t('The vocabulary ID.'), 'name' => $this->t('The name of the vocabulary.'), 'description' => $this->t('The description of the vocabulary.'), 'help' => $this->t('Help text to display for the vocabulary.'), 'relations' => $this->t('Whether or not related terms are enabled within the vocabulary. (0 = disabled, 1 = enabled)'), 'hierarchy' => $this->t('The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)'), 'weight' => $this->t('The weight of the vocabulary in relation to other vocabularies.'), 'parents' => $this->t("The Drupal term IDs of the term's parents."), 'node_types' => $this->t('The names of the node types the vocabulary may be used with.'), ); } /** * {@inheritdoc} */ public function getIds() { $ids['vid']['type'] = 'integer'; return $ids; } }
      
      





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



 <?php /** * @file * Contains \Drupal\migrate_therasomnia\Plugin\migrate\source\Terms. */ namespace Drupal\custom_migration_forum\Plugin\migrate\source; use Drupal\migrate\Row; use Drupal\migrate\Plugin\migrate\source\SqlBase; /** * Drupal 7 taxonomy terms source from database. * * @MigrateSource( * id = "custom_migration_forum_term", * source_provider = "taxonomy" * ) */ class Terms extends SqlBase { /** * {@inheritdoc} */ public function query() { $query = $this->select('taxonomy_term_data', 'td') ->fields('td', ['tid', 'vid', 'name', 'description', 'weight', 'format']) ->fields('tv', ['vid', 'machine_name']) ->distinct(); // Add table for condition on query. $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid'); // Filtered out unnecessary dictionaries. $query->condition('tv.machine_name', 'forums'); return $query; } /** * {@inheritdoc} */ public function fields() { return [ 'tid' => $this->t('The term ID.'), 'vid' => $this->t('Existing term VID'), 'name' => $this->t('The name of the term.'), 'description' => $this->t('The term description.'), 'weight' => $this->t('Weight'), 'parent' => $this->t("The Drupal term IDs of the term's parents."), ]; } /** * {@inheritdoc} */ public function prepareRow(Row $row) { // Find parents for this row. $parents = $this->select('taxonomy_term_hierarchy', 'th') ->fields('th', ['parent', 'tid']); $parents->condition('tid', $row->getSourceProperty('tid')); $parents = $parents->execute()->fetchCol(); $row->setSourceProperty('parent', reset($parents)); return parent::prepareRow($row); } /** * {@inheritdoc} */ public function getIds() { $ids['tid']['type'] = 'integer'; return $ids; } }
      
      





用語と辞書を移行するためのプラグインを作成したら、移行用の構成を作成する必要があります。 これを行うには、 migrate_plus.migration.term.ymlおよびmigrate_plus.migration.vocablary.ymlを作成します。



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



 id: custom_migration_forum_vocabulary label: Taxonomy vocabulary forum migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum source: plugin: custom_migration_forum_vocabulary target: migrate process: vid: - plugin: machine_name source: machine_name - plugin: make_unique_entity_field entity_type: taxonomy_vocabulary field: vid length: 32 migrated: true label: name name: name description: description hierarchy: hierarchy module: module weight: weight destination: plugin: entity:taxonomy_vocabulary
      
      





「ソース」では、プラグインcustom_migration_forum_vocabularyを指定します。これは、 Vocabularyクラスで説明されています。



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



 id: custom_migration_forum_term label: Taxonomy terms forum migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum source: plugin: custom_migration_forum_term target: migrate process: tid: tid vid: plugin: migration migration: custom_migration_forum_vocabulary source: vid name: name description: description weight: weight parent: parent changed: timestamp destination: plugin: entity:taxonomy_term migration_dependencies: required: - custom_migration_forum_vocabulary
      
      





用語をvidに移行する場合、フォーラム辞書IDを取得するマシン名( custom_migration_forum_vocabulary )を指定します。



フォーラムの移行



この記事では、フィールドを追加せずにフォーラムを移行しているという事実に注目したいと思います。 フォーラムの移行を開始する前に、ユーザーを移行する必要があります。ユーザーなしではフォーラムには作成者がいないためです。 カーネルのプラグインを使用してこのような移行を実行しますが、移行グループ「 カスタム移行フォーラム 」に追加して、1つのコマンドで移行全体を開始できるようにします。 次の3つの移行が必要です。



  1. ロールの移行(custom_migration_forum_user_role)
  2. ユーザーの移行(custom_migration_forum_user)
  3. テキスト形式の移行(custom_migration_forum_filter_format)。


ここに3つのymlファイルがあります。



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



 id: custom_migration_forum_user_role label: User roles migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum source: plugin: d7_user_role process: id: - plugin: machine_name source: name - plugin: user_update_8002 label: name permissions: - plugin: static_map source: permissions bypass: true map: 'use PHP for block visibility': 'use PHP for settings' 'administer site-wide contact form': 'administer contact forms' 'post comments without approval': 'skip comment approval' 'edit own blog entries': 'edit own blog content' 'edit any blog entry': 'edit any blog content' 'delete own blog entries': 'delete own blog content' 'delete any blog entry': 'delete any blog content' 'create forum topics': 'create forum content' 'delete any forum topic': 'delete any forum content' 'delete own forum topics': 'delete own forum content' 'edit any forum topic': 'edit any forum content' 'edit own forum topics': 'edit own forum content' - plugin: flatten weight: weight destination: plugin: entity:user_role migration_dependencies: optional: - custom_migration_forum_filter_format
      
      





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



 id: custom_migration_forum_user label: User accounts migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum class: Drupal\user\Plugin\migrate\User source: plugin: d7_user process: uid: uid name: name pass: pass mail: mail created: created access: access login: login status: status timezone: timezone langcode: plugin: user_langcode source: language fallback_to_site_default: false preferred_langcode: plugin: user_langcode source: language fallback_to_site_default: true preferred_admin_langcode: plugin: user_langcode source: language fallback_to_site_default: true init: init roles: plugin: migration_lookup migration: custom_migration_forum_user_role source: roles user_picture: - plugin: default_value source: picture default_value: null - plugin: migration_lookup migration: d7_file destination: plugin: entity:user migration_dependencies: required: - custom_migration_forum_user_role optional: - d7_field_instance - d7_file - language - default_language - user_picture_field_instance - user_picture_entity_display - user_picture_entity_form_display
      
      





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



 id: custom_migration_forum_filter_format label: Filter format configuration migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum source: plugin: d7_filter_format process: format: format name: name cache: cache weight: weight filters: plugin: sub_process source: filters key: '@id' process: id: plugin: filter_id bypass: true source: name map: { } settings: plugin: filter_settings source: settings status: plugin: default_value default_value: true weight: weight destination: plugin: entity:filter_format
      
      





これで、 フォーラム資料の移行のためのプラグインの準備を開始できます。



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



 <?php namespace Drupal\custom_migration_forum\Plugin\migrate\source; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity; use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandler; use Drupal\Core\State\StateInterface; use Drupal\migrate\Plugin\MigrationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Extract forum from Drupal 7 database. * * @MigrateSource( * id = "custom_migration_forum_forum", * ) */ class Forum extends FieldableEntity { /** * The module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ protected $moduleHandler; /** * {@inheritdoc} */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) { parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_manager); $this->moduleHandler = $module_handler; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { return new static( $configuration, $plugin_id, $plugin_definition, $migration, $container->get('state'), $container->get('entity.manager'), $container->get('module_handler') ); } /** * The join options between the node and the node_revisions table. */ const JOIN = 'n.vid = nr.vid'; /** * {@inheritdoc} */ public function query() { // Select node in its last revision. $query = $this->select('node_revision', 'nr') ->fields('n', [ 'nid', 'type', 'language', 'status', 'created', 'changed', 'comment', 'promote', 'sticky', 'tnid', 'translate', ]) ->fields('nr', [ 'vid', 'title', 'log', 'timestamp', ]) ->fields('fb', [ 'body_value', 'body_format', ]); $query->addField('n', 'uid', 'node_uid'); $query->addField('n', 'type', 'node_type'); $query->addField('nr', 'uid', 'revision_uid'); $query->innerJoin('node', 'n', static::JOIN); $query->innerJoin('field_data_body', 'fb', 'n.nid = fb.entity_id'); // If the content_translation module is enabled, get the source langcode // to fill the content_translation_source field. if ($this->moduleHandler->moduleExists('content_translation')) { $query->leftJoin('node', 'nt', 'n.tnid = nt.nid'); $query->addField('nt', 'language', 'source_langcode'); } $this->handleTranslations($query); // Filtered node type forum. $query->condition('n.type', 'forum'); return $query; } /** * {@inheritdoc} */ public function prepareRow(Row $row) { // Get Field API field values. foreach (array_keys($this->getFields('node', 'forum')) as $field) { $nid = $row->getSourceProperty('nid'); $vid = $row->getSourceProperty('vid'); $row->setSourceProperty($field, $this->getFieldValues('node', $field, $nid, $vid)); } // Make sure we always have a translation set. if ($row->getSourceProperty('tnid') == 0) { $row->setSourceProperty('tnid', $row->getSourceProperty('nid')); } return parent::prepareRow($row); } /** * {@inheritdoc} */ public function fields() { $fields = [ 'nid' => $this->t('Node ID'), 'type' => $this->t('Type'), 'title' => $this->t('Title'), 'body_value' => $this->t('Full text of body'), 'body_format' => $this->t('Format of body'), 'node_uid' => $this->t('Node authored by (uid)'), 'revision_uid' => $this->t('Revision authored by (uid)'), 'created' => $this->t('Created timestamp'), 'changed' => $this->t('Modified timestamp'), 'status' => $this->t('Published'), 'promote' => $this->t('Promoted to front page'), 'sticky' => $this->t('Sticky at top of lists'), 'revision' => $this->t('Create new revision'), 'language' => $this->t('Language (fr, en, ...)'), 'tnid' => $this->t('The translation set id for this node'), 'timestamp' => $this->t('The timestamp the latest revision of this node was created.'), ]; return $fields; } /** * {@inheritdoc} */ public function getIds() { $ids['nid']['type'] = 'integer'; $ids['nid']['alias'] = 'n'; return $ids; } /** * Adapt our query for translations. * * @param \Drupal\Core\Database\Query\SelectInterface $query * The generated query. */ protected function handleTranslations(SelectInterface $query) { // Check whether or not we want translations. if (empty($this->configuration['translations'])) { // No translations: Yield untranslated nodes, or default translations. $query->where('n.tnid = 0 OR n.tnid = n.nid'); } else { // Translations: Yield only non-default translations. $query->where('n.tnid <> 0 AND n.tnid <> n.nid'); } } }
      
      





プラグインのymlファイル。



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



 id: custom_migration_forum_forum label: Custom forum migration migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum dependencies: enforced: module: - custom_migration_forum source: plugin: custom_migration_forum_forum node_type: forum target: migrate migration_dependencies: required: - custom_migration_forum_term - custom_migration_forum_user - custom_migration_forum_filter_format process: nid: tnid vid: vid langcode: plugin: default_value source: language default_value: 'en' title: title type: plugin: default_value default_value: forum 'body/value': body_value 'body/format': body_format uid: plugin: migration migration: custom_migration_forum_user source: node_uid status: status created: created changed: changed promote: promote sticky: sticky revision_uid: revision_uid revision_log: log revision_timestamp: timestamp taxonomy_forums: plugin: migration migration: custom_migration_forum_term source: taxonomy_forums destination: plugin: entity:node
      
      





最後に残っているのは、トピックに関するコメントです(トピックなしでどうすればよいでしょうか?)

プラグインを作成し、構成に記述します。



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



 <?php /** * @file * Contains \Drupal\custom_migration_forum\Plugin\migrate\source\ForumComment. */ namespace Drupal\custom_migration_forum\Plugin\migrate\source; use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity; /** * Drupal 7 comment forum source from database. * * @MigrateSource( * id = "custom_migration_forum_forum_comment", * source_provider = "comment", * ) */ class ForumComment extends FieldableEntity { /** * {@inheritdoc} */ public function query() { $query = $this->select('comment', 'c')->fields('c'); $query->innerJoin('node', 'n', 'c.nid = n.nid'); $query->addField('n', 'type', 'node_type'); $query->addField('n', 'nid'); $query->condition('n.type', 'forum'); $query->orderBy('c.created'); return $query; } /** * {@inheritdoc} */ public function prepareRow(Row $row) { $cid = $row->getSourceProperty('cid'); $node_type = $row->getSourceProperty('node_type'); $comment_type = 'comment_node_' . $node_type; $row->setSourceProperty('comment_type', 'comment_forum'); foreach (array_keys($this->getFields('comment', $comment_type)) as $field) { $row->setSourceProperty($field, $this->getFieldValues('comment', $field, $cid)); } return parent::prepareRow($row); } /** * {@inheritdoc} */ public function fields() { return [ 'cid' => $this->t('Comment ID.'), 'pid' => $this->t('Parent comment ID. If set to 0, this comment is not a reply to an existing comment.'), 'nid' => $this->t('The {node}.nid to which this comment is a reply.'), 'uid' => $this->t('The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.'), 'subject' => $this->t('The comment title.'), 'comment' => $this->t('The comment body.'), 'hostname' => $this->t("The author's host name."), 'created' => $this->t('The time that the comment was created, as a Unix timestamp.'), 'changed' => $this->t('The time that the comment was edited by its author, as a Unix timestamp.'), 'status' => $this->t('The published status of a comment. (0 = Published, 1 = Not Published)'), 'format' => $this->t('The {filter_formats}.format of the comment body.'), 'thread' => $this->t("The vancode representation of the comment's place in a thread."), '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."), '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."), '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."), 'type' => $this->t("The {node}.type to which this comment is a reply."), ]; } /** * {@inheritdoc} */ public function getIds() { $ids['cid']['type'] = 'integer'; return $ids; } }
      
      





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



 id: custom_migration_forum_forum_comment label: Comments forum migration_group: Custom Migration Forum dependencies: enforced: module: - custom_migration_forum source: plugin: custom_migration_forum_forum_comment target: migrate constants: entity_type: node process: cid: cid pid: plugin: migration_lookup migration: custom_migration_forum_forum_comment source: pid entity_id: nid entity_type: 'constants/entity_type' comment_type: comment_type field_name: comment_type subject: subject uid: uid name: name mail: mail homepage: homepage hostname: hostname created: created changed: changed status: status thread: thread comment_body: comment_body destination: plugin: entity:comment migration_dependencies: required: - custom_migration_forum_forum
      
      





移行の開始



これで移行の準備が整いました。 Drupal 8のサイトの構成ファイルにDrupal 7データベースを追加します。これは、settings.phpファイル(または、設定を接続する別のファイルで行います。このsettings.local.phpファイルがあります):



 <?php $databases['migrate']['default'] = array ( 'database' => 'Drupal_7', 'username' => 'root', 'password' => 'root', 'prefix' => '', 'host' => 'localhost', 'port' => '3306', 'namespace' => 'Drupal\Core\Database\Driver\mysql', 'driver' => 'mysql', );
      
      





次のモジュールが含まれます。



 drush en migrate migrate_drupal migrate_plus migrate_tools taxonomy forum -
      
      





GitHubからモジュールをダウンロードした場合は、単純に有効にすることができます。



 drush en custom_migration_forum -y
      
      





画像



Drushを介した移行



利用可能な移行のリストを確認します。



 drush ms
      
      





画像



1つのチームですべての移行を開始します。



 drush mim --group="Custom Migration Forum"
      
      





画像



移行の変更を取り消すために、drush mrコマンドがあります。



 drush mr --group="Custom Migration Forum"
      
      





画像



また、移行中にエラーが発生することがあり、 インポートまたはロールバックのステータスで移行がフリーズする場合があります。 移行ステータスをリセットするには、次を実行する必要があります。



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





ここで、 custom_migration_forum_forumは移行IDです。



フォーラムの移行が完了しました。 その結果、ユーザーとトピックに関するコメントを含む完全に移行されたフォーラムができました。



画像







All Articles