Start from a fresh install of Drupal 8, in order to generate all the required YML files to use inside your custom migrate modules you must first install all these modules:
Drupal Upgrade
Drush support for direct upgrades from older Drupal versions.
Migrate
Handles migrations
Migrate Drupal
Contains migrations from older Drupal versions.
Migrate Drupal UI
Provides a user interface for migrating from older Drupal versions.
Migrate Plus
Enhancements to core migration support
Migrate Tools
Tools to assist in developing and running migrations.
When you run the migrate upgrade you also need to install all the modules in Drupal 8 for the entities that you want to migrate. You could set a bash script to run these drush commands:
drush en migrate_upgrade -y drush en migrate_tools -y drush en module_filter -y drush en migrate_plus -y drush en migrate_tools -y drush en pathauto -y drush en field_collection -y drush en backup_migrate -y drush en metatag -y drush en migrate_drupal_ui -y drush en migrate_source_csv -y drush en webform -y drush en webform_ui -y drush en features -y
You can generate all your YML configuration files using the Drupal Upgrade module which provides this Drush command:
drush migrate-upgrade --legacy-db-url=mysql://d7_user:d7_pass@127.0.0.1/d7_database_name --legacy-root=/pathtodrupalsix --configure-only
You can make it even simpler if you add this to your settings.php a database connection called 'upgrade' so the it will read the details of your drupal 7 database connection directly from:
// Database entry for `drush migrate-upgrade --configure-only`
$databases['upgrade']['default'] = array (
'database' => 'd7_database_name',
'username' => 'd7_user',
'password' => 'd7_pass',
'prefix' => '',
'host' => 'localhost',
'port' => '3306',
'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
'driver' => 'mysql',
);
The you can run it without passing the DB connection details:
drush migrate-upgrade --configure-only
In order to export all your YML configuration files use all the following drush commands:
drush config-export --destination=/tmp/migrate
$ cd /tmp/migrate
$ ls
migrate_plus.migration.upgrade_block_content_body_field.yml migrate_plus.migration.upgrade_d7_search_settings.yml
migrate_plus.migration.upgrade_block_content_type.yml migrate_plus.migration.upgrade_d7_shortcut.yml
migrate_plus.migration.upgrade_d7_block.yml migrate_plus.migration.upgrade_d7_shortcut_set.yml
migrate_plus.migration.upgrade_d7_comment.yml migrate_plus.migration.upgrade_d7_shortcut_set_users.yml
migrate_plus.migration.upgrade_d7_comment_entity_display.yml migrate_plus.migration.upgrade_d7_system_authorize.yml
migrate_plus.migration.upgrade_d7_comment_entity_form_display.yml migrate_plus.migration.upgrade_d7_system_cron.yml
migrate_plus.migration.upgrade_d7_comment_entity_form_display_subject.yml migrate_plus.migration.upgrade_d7_system_date.yml
migrate_plus.migration.upgrade_d7_comment_field.yml migrate_plus.migration.upgrade_d7_system_file.yml
migrate_plus.migration.upgrade_d7_comment_field_instance.yml migrate_plus.migration.upgrade_d7_system_mail.yml
migrate_plus.migration.upgrade_d7_comment_type.yml migrate_plus.migration.upgrade_d7_system_performance.yml
migrate_plus.migration.upgrade_d7_custom_block.yml migrate_plus.migration.upgrade_d7_taxonomy_term_tags.yml
migrate_plus.migration.upgrade_d7_dblog_settings.yml migrate_plus.migration.upgrade_d7_taxonomy_vocabulary.yml
migrate_plus.migration.upgrade_d7_field.yml migrate_plus.migration.upgrade_d7_url_alias.yml
migrate_plus.migration.upgrade_d7_field_formatter_settings.yml migrate_plus.migration.upgrade_d7_user.yml
migrate_plus.migration.upgrade_d7_field_instance.yml migrate_plus.migration.upgrade_d7_user_flood.yml
migrate_plus.migration.upgrade_d7_field_instance_widget_settings.yml migrate_plus.migration.upgrade_d7_user_mail.yml
migrate_plus.migration.upgrade_d7_file.yml migrate_plus.migration.upgrade_d7_user_role.yml
migrate_plus.migration.upgrade_d7_filter_format.yml migrate_plus.migration.upgrade_d7_view_modes.yml
migrate_plus.migration.upgrade_d7_filter_settings.yml migrate_plus.migration.upgrade_file_settings.yml
migrate_plus.migration.upgrade_d7_global_theme_settings.yml migrate_plus.migration.upgrade_menu_settings.yml
migrate_plus.migration.upgrade_d7_image_settings.yml migrate_plus.migration.upgrade_search_page.yml
migrate_plus.migration.upgrade_d7_image_styles.yml migrate_plus.migration.upgrade_system_image.yml
migrate_plus.migration.upgrade_d7_menu.yml migrate_plus.migration.upgrade_system_image_gd.yml
migrate_plus.migration.upgrade_d7_menu_links.yml migrate_plus.migration.upgrade_system_logging.yml
migrate_plus.migration.upgrade_d7_node_article.yml migrate_plus.migration.upgrade_system_maintenance.yml
migrate_plus.migration.upgrade_d7_node_page.yml migrate_plus.migration.upgrade_system_rss.yml
migrate_plus.migration.upgrade_d7_node_revision_article.yml migrate_plus.migration.upgrade_system_site.yml
migrate_plus.migration.upgrade_d7_node_revision_page.yml migrate_plus.migration.upgrade_taxonomy_settings.yml
migrate_plus.migration.upgrade_d7_node_revision_service.yml migrate_plus.migration.upgrade_text_settings.yml
migrate_plus.migration.upgrade_d7_node_revision_webform.yml migrate_plus.migration.upgrade_update_settings.yml
migrate_plus.migration.upgrade_d7_node_service.yml migrate_plus.migration.upgrade_user_picture_entity_display.yml
migrate_plus.migration.upgrade_d7_node_settings.yml migrate_plus.migration.upgrade_user_picture_entity_form_display.yml
migrate_plus.migration.upgrade_d7_node_title_label.yml migrate_plus.migration.upgrade_user_picture_field.yml
migrate_plus.migration.upgrade_d7_node_type.yml migrate_plus.migration.upgrade_user_picture_field_instance.yml
migrate_plus.migration.upgrade_d7_node_webform.yml migrate_plus.migration_group.migrate_drupal_7.yml
Copy the relevant YML configuration files inside your module(s): /custom/
Most of the times you may need to apply some customisation. For example in Maria Consulting we did not need to use all the above YML files. We set up these 2 custom modules:
migrate_maria and migrate_maria_types. I placed only 5 yml files inside migrate_maria/config/install in order to start migrating node_service, taxonomy_vocabulary, taxonomy_term, url_alias and files. The migrate_maria.info.yml looks like:
name: migrate_maria
type: module
description: Migrate Maria Consulting content to D8
core: 8.x
package: Custom
dependencies:
- migrate_plus
- migrate_drupal
Remember that your module must depends from migrate_plus and migrate_drupal. After copying these 5 files inside migrate_maria/config/install in order to make them more custom I renamed these part of the file name from "upgrade_d7" to be "maria_d7": migrate_plus.migration.maria_d7_node_service, migrate_plus.migration.maria_d7_taxonomy_vocabulary, migrate_plus.migration.maria_d7_taxonomy_term, migrate_plus.migration.maria_d7_url_alias and migrate_plus.migration.maria_d7_file
Then I also changed the content of the file to reflect the change of the name. For example the content of migrate_plus.migration.maria_d7_taxonomy_term became:
uuid: 5325b50e-7f9c-4ed4-b139-d09debb43497
langcode: en
status: true
dependencies: { }
id: maria_d7_taxonomy_term
migration_tags:
- 'Drupal 7'
migration_group: migrate_maria
label: 'Taxonomy terms (Services)'
source:
plugin: d7_taxonomy_term
bundle: tags
process:
tid: tid
vid:
plugin: migration
migration: maria_d7_taxonomy_vocabulary
source: vid
name: name
description/value: description
description/format: format
weight: weight
parent_id:
-
plugin: skip_on_empty
method: process
source: parent
-
plugin: migration
migration: maria_d7_taxonomy_term
parent:
plugin: default_value
default_value: 0
source: '@parent_id'
changed: timestamp
field_right_body:
plugin: iterator
source: field_right_body
process:
value: value
format:
-
plugin: static_map
bypass: true
source: format
map:
- null
-
plugin: skip_on_empty
method: process
-
plugin: migration
migration:
- upgrade_d6_filter_format
- maria_d7_filter_format
source: format
destination:
plugin: 'entity:taxonomy_term'
default_bundle: tags
migration_dependencies:
required:
- maria_d7_taxonomy_vocabulary
optional:
- maria_d7_field_instance
- maria_d7_taxonomy_vocabulary
- maria_d7_taxonomy_term_tags
- upgrade_d6_filter_format
- maria_d7_filter_format
- maria_d7_taxonomy_term
As you can see I changed id: to reflect the name of the file: maria_d7_taxonomy_term
, migration_group: migrate_maria, I defined an new plugin: d7_taxonomy_term. Remember to create an install file: migrate_maria.install with
hook_uninstall() in order to delete these configuration in case you need to uninstall the module:
/**
* Implements hook_uninstall().
*/
function migrate_maria_uninstall() {
\Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_node_service')->delete();
\Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_taxonomy_term')->delete();
\Drupal::configFactory()->getEditable('migrate_plus.migration.mria_d7_taxonomy_vocabulary')->delete();
\Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_url_alias')->delete();
\Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_file')->delete();
}
As I mentioned earlier I defined an new plugin: d7_taxonomy_term.In order to define it, I create this file:
migrate_maria\Plugin\migrate\source\Term.php:
<?php
/**
* @file
* Contains \Drupal\migrate_maria\Plugin\migrate\source\Term.
*/
namespace Drupal\migrate_maria\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
/**
* Drupal 7 taxonomy terms source from database.
*
* @todo Support term_relation, term_synonym table if possible.
*
* @MigrateSource(
* id = "maria_d7_taxonomy_term",
* source_provider = "taxonomy"
* )
*/
class Term extends SqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('taxonomy_term_data', 'td')
->fields('td', array('tid', 'vid', 'name', 'description', 'weight', 'format'))
->distinct();
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
return array(
'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', array('parent', 'tid'))
->condition('tid', $row->getSourceProperty('tid'))
->execute()
->fetchCol();
$row->setSourceProperty('parent', $parents);
return parent::prepareRow($row);
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['tid']['type'] = 'integer';
return $ids;
}
}
The reason why I had to create this custom plugin was because the original one (upgrade_d7_taxonomy_term) defined by the core (please see Drupal\migrate_drupal\Plugin\migrate\source) was not setting the parents (default parent was 0!). As you see in the PrepareRow() method I added the logic to get the parents from the taxonomy_term_hierarchy
table so that my taxonomy terms keep the hierarchy structure.
Now that all my migrate configuration are set up, it comes the most exiting part of the process it self: RUN the drush mi commands! Obviously the first thing to do is to enable our custom module migrate_maria:
drush en migrate_maria -y
dursh cr
drush ms
Group: Import Maria Consulting Content from Drupal 7 (migrate_maria) Status Total Imported Unprocessed Last imported
upgrade_d7_file Idle 22 22 0 2017-03-11
upgrade_d7_taxonomy_vocabulary Idle 1 1 0 2017-03-11
upgrade_d7_url_alias Idle 57 57 0 2017-03-11
maria_d7_taxonomy_term Idle 29 29 0 2017-03-11
upgrade_d7_node_service Idle 22 22 0 2017-03-11
For example when I ran “drush mi maria_d7_taxonomy_term”
magically 29 terms were imported and the hierarchy was kept! You can also run all these migrations in only one command using the group option: “drush mi --group=migrate_maria”.
In conclusion I have to admit that initially I found the migration process very different from the way I was used to know it from the previous version of Drupal, but actually in Drupal 8 I found it much simpler because all the settings are all in YML files which are very easy to configure and there is a complete separation between configuration and PHP code: all the behaviors are added in Class which defines Plugin and they have a very simple Interface and all the methods are self explanatory: query(), fields(), prepareRow(Row $row), getIds()
. There are so many Plugins already defined in the core that actually you need to do very little to make your migration works! Actually for Maria Consulting I had only to define one custom Plugin which was Term.php.