How to Prevent Duplicate Terms During a Drupal 8 Migration

In this post I will show a custom process plugin that I created to migrate taxonomy terms. The plugin handles the creation of new terms and prevents duplicates.

Below is a portion of the migration template. In the example, I am migrating new terms into keywords vocabulary via field_keywords field.

  field_keywords:
    -
      plugin: existing_term
      # Destination (Drupal) vocabulary name
      vocabulary: keywords
      # Source query should return term name
      source: term_name
    -
      plugin: skip_on_empty
      method: row

This is the source code for the process plugin.

<?php

namespace Drupal\my_module\Plugin\migrate\process;

use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipProcessException;
use Drupal\migrate\Row;
use Drupal\taxonomy\Entity\Term;

/**
 * Check if term exists and create new if doesn't.
 *
 * @MigrateProcessPlugin(
 *   id = "existing_term"
 * )
 */
class ExistingTerm extends ProcessPluginBase {

  /**
   * {@inheritdoc}
   */
  public function transform($term_name, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    $vocabulary = $this->configuration['vocabulary'];
    if (empty($term_name)) {
      throw new MigrateSkipProcessException();
    }
    if ($tid = $this->getTidByName($term_name, $vocabulary)) {
      $term = Term::load($tid);
    }
    else {
      $term = Term::create([
        'name' => $term_name, 
        'vid'  => $vocabulary,
      ])->save();
      if ($tid = $this->getTidByName($term_name, $vocabulary)) {
        $term = Term::load($tid);
      }
    }
    return [
      'target_id' => is_object($term) ? $term->id() : 0,
    ];
  }

  /**
   * Load term by name.
   */
  protected function getTidByName($name = NULL, $vocabulary = NULL) {
    $properties = [];
    if (!empty($name)) {
      $properties['name'] = $name;
    }
    if (!empty($vocabulary)) {
      $properties['vid'] = $vocabulary;
    }
    $terms = \Drupal::entityManager()->getStorage('taxonomy_term')->loadByProperties($properties);
    $term = reset($terms);
    return !empty($term) ? $term->id() : 0;
  }

}

The logic of the plugin is very simple. Please let me know in comments or questions. Also, please share any problems you've had during your content migrations and how you solved them.