Drupal 8 Migrations, part 3: Migrating Taxonomies from Drupal 7
Drupal 8 Migrations, part 3: Migrating Taxonomies from Drupal 7
December 10th, 2014
Keith Dechant
Drupal 8 provides a flexible, plugin-based architecture for migrating data into a site. In Part 2 of this series, we explored how to migrate users from a Drupal 7 site. We will now expand on this by migrating Taxonomy vocabularies and terms from a Drupal 7 site into Drupal 8.
This article continues our work from Part 2. The code examples pick up where that post left off. If you are trying this code out yourself, it is recommended to start building your custom migration module according to the examples in that post.
Migrating Taxonomy Vocabularies
The Migrate Drupal module (in Drupal 8 core) already contains a migration definition and source plugins to migrate taxonomy data from Drupal 6 to Drupal 8. All we need to do is to adapt the existing code to work with Drupal 7.
The migration definition:
Starting with the "Migrate Custom" module we created in Part 2, we now add the following configuration file.
modules/migrate_custom/config/install/migrate.migration.custom_taxonomy_vocabulary.yml
id: custom_taxonomy_vocabulary
label: Drupal 7 taxonomy vocabularies
migration_groups:
- Drupal 7
source:
plugin: custom_taxonomy_vocabulary
process:
vid:
-
plugin: machine_name
source: machine_name
-
plugin: dedupe_entity
entity_type: taxonomy_vocabulary
field: vid
length: 32
label: name
name: name
description: description
hierarchy: hierarchy
module: module
weight: weight
destination:
plugin: entity:taxonomy_vocabulary
Here we have examples of a few plugins not seen in the previous post:
machine_name
converts the string into a valid machine name.dedupe_entity
prevents machine name conflicts, which would cause imported data to overwrite existing data. For example, a machine name "foo" would be renamed to "foo_2" if name "foo" already existed.
The source plugin
To define the source of our vocabulary data, we create a new file modules/migrate_custom/src/Plugin/migrate/source/Vocabulary.php
with the following contents:
<?php
/**
* @file
* Contains \Drupal\migrate_custom\Plugin\migrate\source\Vocabulary.
*/
namespace Drupal\migrate_custom\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* Drupal 7 vocabularies source from database.
*
* @MigrateSource(
* id = "custom_taxonomy_vocabulary",
* source_provider = "taxonomy"
* )
*/
class Vocabulary extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('taxonomy_vocabulary', 'v')
->fields('v', array(
'vid',
'name',
'description',
'hierarchy',
'module',
'weight',
'machine_name'
));
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;
}
}
Note: this file was adapted from the Drupal 6 version in Drupal 8 core. For the original file, see core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Vocabulary.php
The structure of this file is similar to the User source plugin in the previous article. However, because all the data we need is stored in the `taxonomy_vocabulary` table in the source database, we do not need to define the prepareRow()
method.
Migrating Taxonomy Terms
We can use a second migration definition to migrate our taxonomy terms. Create the following file:
modules/migrate_custom/config/install/migrate.migration.custom_taxonomy_term.yml
id: custom_taxonomy_term
label: Drupal 7 taxonomy terms
migration_groups:
- Drupal 7
source:
plugin: custom_taxonomy_term
process:
tid: tid
vid:
plugin: migration
migration: custom_taxonomy_vocabulary
source: vid
name: name
description: description
weight: weight
parent:
-
plugin: skip_process_on_empty
source: parent
-
plugin: migration
migration: custom_taxonomy_term
changed: timestamp
destination:
plugin: entity:taxonomy_term
migration_dependencies:
required:
- custom_taxonomy_vocabulary
In this migration, we make use of the migration
process plugin for two of our properties, the vocabulary ID and the parent term ID. This preserves these references in case the referenced entity's ID or machine name changed during the import.
Some machine names and/or IDs will likely change when running your import. This is to be expected, especially because Drupal 8 stores taxonomy vocabularies in the 'config' table, where they are accessed by their machine names instead of by the numeric IDs used in Drupal 7. Fortunately for us, the Migrate module records a map of the old and new IDs in the database. We can then use the migration
source plugin to easily look up the old ID or machine name.
The source plugin
To define the source of our term data, we create a new file modules/migrate_custom/src/Plugin/migrate/source/Term.php
with the following contents:
<?php
/**
* @file
* Contains \Drupal\migrate_custom\Plugin\migrate\source\Term.
*/
namespace Drupal\migrate_custom\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* Drupal 7 taxonomy terms source from database.
*
* @todo Support term_relation, term_synonym table if possible.
*
* @MigrateSource(
* id = "custom_taxonomy_term",
* source_provider = "taxonomy"
* )
*/
class Term extends DrupalSqlBase {
/**
* {@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;
}
}
Reloading the configuration
Remember that migrations are configuration entities. To reload the configuration, we need to uninstall and reinstall our module. Here's a handy Drush command to do this:
drush pm-uninstall migrate_custom -y && drush en migrate_custom
Running the migration
We need to add some new lines to our manifest.yml file:
# A D7 user and taxonomy migration, with dependencies.
- custom_user
- custom_taxonomy_vocabulary
- custom_taxonomy_term
As we did with our user migration, we now run the migration using Drush.
drush migrate-manifest manifest.yml --legacy-db-url=mysql://{dbuser}:{dbpass}@localhost/{dbname}
When including multiple migrations in a single manifest, be aware that drush migrate-manifest
doesn't always run them in the order you specified. If, for example, your taxonomy migrations are being run before your user migration, and your taxonomy terms end up missing their UIDs, you might need to create two separate manifest files, to give yourself better control over the order.
Next post: Migrating Nodes from Drupal 7.