Migrate Drupal 7 to Drupal 8 / 9 using aliases

設定來源資料庫

<site>\sites\default\Settings.php
// Database entry needed by Migrate D7
// This is the source database connection d7. needs 'upgrade' key
$databases['upgrade']['default'] = array (
  'database' => 'drupal7',
  'username' => 'drupal7',
  'password' => '$webdbpw',(1)
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

// Database entry needed by by Migrate D7 (2)
// This is the target database connection d8 needs 'migrate' key
$databases['migrate']['default'] = array (
  'database' => 'd7d8db',
  'username' => 'd7d8db',
  'password' => '$webdbpw',(1)
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);
1 $webdbpw 修改為實際密碼。
2 沒有作用,但必須有該資料庫,執行遷移時才不會出錯!
建立 d7d8db 資料庫
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -N<<EOF
drop database if exists d7d8db;
create database d7d8db;
grant all privileges on d7d8db.* to 'd7d8db'@'localhost' identified by '$webdbpw';
use mysql;
update user set plugin='mysql_native_password' where user='d7d8db';
EOF

migrate_d7alias

/var/www/drupal/modules/migrate_d7alias 目錄結構
migrate_d7alias
├── modules
│   ├── migrate_d7_guide
│   │   ├── config
│   │   │   └─ install
│   │   │      ├─ migrate_plus.migration_group.d7_guide.yml
│   │   │      ├─ migrate_plus.migration.d7_guide_base.yml
│   │   │      └─ migrate_plus.migration.d7_guide_trans.yml
│   │   ├─ migrate_d7_guide.info.yml
│   │   └─ migrate_d7_guide.install
│   └─ migrate_d7_others (1)
├── src
│   └─ Plugin
│      └─ migrate
│         └─ source
│            └─ D7alias.php
└─ migrate_d7alias.info.yml
1 若要遷移另一組不同的 Content types,migrate_d7_guide 複製至 migrate_d7_others,將 migrate_d7_others 檔案及內容的 guide 改成 othersGuide 改成 Others
migrate_d7alias/modules/migrate_d7_guide/config/install/migrate_plus.migration_group.d7_guide.yml
name: Migrate D7 Alias
type: module
description: Migrate D7 Alias to D8
core_version_requirement: ^8.8 || ^9
package: Custom
dependencies:
  - migrate_plus
  - migrate_drupal
migrate_d7alias/modules/migrate_d7_guide/config/install/migrate_plus.migration.d7_guide_base.yml
langcode: en
status: true
dependencies: {  }
id: d7_guide_base
migration_tags: null
migration_group: d7_guide
label: Guide base
source:
  plugin: migrate_d7alias
  node_type: guide (1)

destination:
  plugin: 'entity:node'
  default_bundle: guide (2)

process:
  nid: nid
  langcode: language

  title: title
  status: status
  created: created
  changed: changed
  promote: promote
  field_url: alias (3)
  sticky: sticky
  body:
    plugin: sub_process
    source: body
    process:
      value: value
      format:
        plugin: default_value
        default_value: full_html (4)

  path/pathauto:
    plugin: default_value
    default_value: 0 # Disable pathauto (5)

  path/alias: alias # alias to path_alias
1 guide 為 D7 的 Content types (Machine name)。
2 guide 為 D8 的 Content types (Machine name)。
3 假設 D8 的 guide 中有 field_url 欄位,將 D7 的 alias 填入該欄位。註: 沒有該欄位並不會引發錯誤。
4 指定 Text formats and editors (<site>/admin/config/content/formats) 中的 Machine name: full_html
5 取消 Pathauto 自動產生網址。註: 沒有安裝 Pathauto 並不會引發錯誤。
migrate_d7alias/modules/migrate_d7_guide/config/install/migrate_plus.migrate_plus.migration.d7_guide_trans.yml
langcode: en
status: true
dependencies: {  }
id: d7_guide_trans
migration_tags: null
migration_group: d7_guide
label: Guide translations
source:
  plugin: migrate_d7alias
  node_type: guide
  translations: true

destination:
  plugin: 'entity:node'
  default_bundle: guide
  translations: true

process:
  nid:
    plugin: migration_lookup
    source: tnid
    migration: d7_guide_base

  langcode: language
  title: title
  status: status
  created: created
  changed: changed
  promote: promote
  sticky: sticky
  body:
    plugin: sub_process
    source: body
    process:
      value: value
      format:
        plugin: default_value
        default_value: full_html

  path/pathauto:
    plugin: default_value
    default_value: 0 # Disable pathauto

  path/alias: alias # alias to path_alias

migration_dependencies:
  required:
    - d7_guide_base
migrate_d7alias/modules/migrate_d7_guide/migrate_d7_guide.info.yml
name: Migrate D7 Guide
type: module
description: Migrate D7 Guide to D8
core_version_requirement: ^8.8 || ^9
package: Custom
dependencies:
  - migrate_d7alias
migrate_d7alias/modules/migrate_d7_guide/migrate_d7_guide.install
<?php

/**
 * @file
 * Contains install and uninstall hooks.
 */

 /**
  * Implements hook_uninstall().
  */

function migrate_d7_guide_uninstall() {
  \Drupal::entityTypeManager()->getStorage('migration')->load('d7_guide_base')->delete();
  \Drupal::entityTypeManager()->getStorage('migration')->load('d7_guide_trans')->delete();
  \Drupal::entityTypeManager()->getStorage('migration_group')->load('d7_guide')->delete();
}
migrate_d7alias/src/Plugin/migrate/source/D7alias.php
<?php
/**
 * @file
 * Contains \Drupal\migrate_d7alias\Plugin\migrate\source\D7alias.
 */

namespace Drupal\migrate_d7alias\Plugin\migrate\source;

use Drupal\migrate\Row;
use Drupal\node\Plugin\migrate\source\d7\Node;

/**
 * Migration Source for fruit nodes
 *
 * @MigrateSource(
 *   id = "migrate_d7alias"
 * )
 */
class D7alias extends Node
{
    /**
     * {@inheritdoc}
     */
    public function prepareRow(Row $row)
    {
        // Include path alias.
        $nid = $row->getSourceProperty('nid');
        $query = $this->select('url_alias', 'ua')->fields('ua', ['alias']);
        $query->condition('ua.source', 'node/' . $nid);
        $alias = $query->execute()->fetchField();
        if (!empty($alias)) {
            $row->setSourceProperty('alias', '/' . $alias);
        }
        return parent::prepareRow($row);
    }
}
migrate_d7alias/migrate_d7alias.info.yml
name: Migrate D7 Alias
type: module
description: Migrate D7 Alias to D8
core_version_requirement: ^8.8 || ^9
package: Custom
dependencies:
  - migrate_plus
  - migrate_drupal

安裝及遷移

安裝 migrate_d7alias
site=drupal
cd /var/www/$site
composer require drupal/migrate_tools drupal/migrate_plus
drush en migrate_tools -y

drush en migrate_d7alias migrate_d7_guide -y
# drush en migrate_d7_others -y

chwww $site
修改 d7_guide_base.yml d7_guide_trans.yml 後,需要重裝
drush pmu migrate_d7_guide
drush en migrate_d7_guide
遷移 D7
site=drupal
cd /var/www/$site

drush migrate:status
# ---------------------------- ----------------
#  Group                        Migration ID
# ---------------------------- ----------------
#  D7 guide imports (d7_guide)  d7_guide_base
#  D7 guide imports (d7_guide)  d7_guide_trans


drush state:set system.maintenance_mode 1 --input-format=integer
# drush cr

# 將 link__uri 修改為 fake,避免刪除 node 時同時刪除 menu。
drush sql-query "update menu_link_content_data set link__uri=concat(link__uri,'-fake') where link__uri not like '%-fake'"
drush sql-query "update menu_link_content_field_revision set link__uri=concat(link__uri,'-fake') where link__uri not like '%-fake'"
drush cr

drush migrate:rollback --group=d7_guide (1)
drush migrate-import --group=d7_guide --update

# 將 link__uri 回復
drush sql-query "update menu_link_content_data set link__uri=replace(link__uri,'-fake','')"
drush sql-query "update menu_link_content_field_revision set link__uri=replace(link__uri,'-fake','')"
# drush cr

# 重建索引
drupal cron:execute

drush state:set system.maintenance_mode 0 --input-format=integer
drush cr
1 Migrate 前先刪除全部,避免出現重複文章。

已將 D7 的 alias 一併遷移至 D8,照理說不會裝 Pathauto 才對。如果有安裝,在執行前必須將 Pathauto patterns 禁能,否則會出錯。 如錯誤可能為 Migration d7_guide_trans is busy with another operation: Importing

出錯時執行下列
drush migrate:stop d7_guide_base
drush migrate:stop d7_guide_trans
drush migrate:reset-status d7_guide_trans
drush migrate:reset-status d7_guide_base