Issue #2824610 by quietone, Jo Fitzgerald, ohthehugemanatee, heddn, catch: Rename DedupeBase/DedupeEntity process plugins to MakeUnique and add documentation
							parent
							
								
									3c72c97d0b
								
							
						
					
					
						commit
						eef585f6aa
					
				| 
						 | 
				
			
			@ -13,7 +13,8 @@ process:
 | 
			
		|||
    source: status
 | 
			
		||||
  id:
 | 
			
		||||
    # We need something unique, so aggregator, aggregator_1 etc will do.
 | 
			
		||||
    plugin: dedupe_entity
 | 
			
		||||
    plugin: make_unique_entity_field
 | 
			
		||||
 | 
			
		||||
    entity_type: block
 | 
			
		||||
    field: id
 | 
			
		||||
    postfix: _
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ process:
 | 
			
		|||
      plugin: machine_name
 | 
			
		||||
      source: category
 | 
			
		||||
    -
 | 
			
		||||
      plugin: dedupe_entity
 | 
			
		||||
      plugin: make_unique_entity_field
 | 
			
		||||
      entity_type: contact_form
 | 
			
		||||
      field: id
 | 
			
		||||
      length: 32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ process:
 | 
			
		|||
      plugin: machine_name
 | 
			
		||||
      source: presetname
 | 
			
		||||
    -
 | 
			
		||||
      plugin: dedupe_entity
 | 
			
		||||
      plugin: make_unique_entity_field
 | 
			
		||||
      entity_type: image_style
 | 
			
		||||
      field: name
 | 
			
		||||
      length: 32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,11 +2,8 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\migrate\Plugin\migrate\process;
 | 
			
		||||
 | 
			
		||||
use Drupal\migrate\ProcessPluginBase;
 | 
			
		||||
use Drupal\migrate\MigrateExecutableInterface;
 | 
			
		||||
use Drupal\migrate\Row;
 | 
			
		||||
use Drupal\migrate\MigrateException;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
@trigger_error('The ' . __NAMESPACE__ . ' \DedupeEntityBase is deprecated in
 | 
			
		||||
Drupal 8.4.x and will be removed before Drupal 9.0.0. Instead, use ' . __NAMESPACE__ . ' \MakeUniqueEntityFieldBase', E_USER_DEPRECATED);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This abstract base contains the dedupe logic.
 | 
			
		||||
| 
						 | 
				
			
			@ -17,41 +14,9 @@ use Drupal\Component\Utility\Unicode;
 | 
			
		|||
 * and incremented until a unique value is created.
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://www.drupal.org/node/2345929 Online handbook documentation for dedupebase process plugin @endlink
 | 
			
		||||
 */
 | 
			
		||||
abstract class DedupeBase extends ProcessPluginBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
 | 
			
		||||
    $i = 1;
 | 
			
		||||
    $postfix = isset($this->configuration['postfix']) ? $this->configuration['postfix'] : '';
 | 
			
		||||
    $start = isset($this->configuration['start']) ? $this->configuration['start'] : 0;
 | 
			
		||||
    if (!is_int($start)) {
 | 
			
		||||
      throw new MigrateException('The start position configuration key should be an integer. Omit this key to capture from the beginning of the string.');
 | 
			
		||||
    }
 | 
			
		||||
    $length = isset($this->configuration['length']) ? $this->configuration['length'] : NULL;
 | 
			
		||||
    if (!is_null($length) && !is_int($length)) {
 | 
			
		||||
      throw new MigrateException('The character length configuration key should be an integer. Omit this key to capture the entire string.');
 | 
			
		||||
    }
 | 
			
		||||
    // Use optional start or length to return a portion of deduplicated value.
 | 
			
		||||
    $value = Unicode::substr($value, $start, $length);
 | 
			
		||||
    $new_value = $value;
 | 
			
		||||
    while ($this->exists($new_value)) {
 | 
			
		||||
      $new_value = $value . $postfix . $i++;
 | 
			
		||||
    }
 | 
			
		||||
    return $new_value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * This is a query checking the existence of some value.
 | 
			
		||||
 *
 | 
			
		||||
   * @param mixed $value
 | 
			
		||||
   *   The value to check.
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if the value exists.
 | 
			
		||||
 * @deprecated in Drupal 8.4.x and will be removed in Drupal 9.0.x. Use
 | 
			
		||||
 *   \Drupal\migrate\Plugin\migrate\process\MakeUniqueBase instead.
 | 
			
		||||
 */
 | 
			
		||||
  abstract protected function exists($value);
 | 
			
		||||
 | 
			
		||||
abstract class DedupeBase extends MakeUniqueBase {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,8 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\migrate\Plugin\migrate\process;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 | 
			
		||||
use Drupal\migrate\Plugin\MigrationInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
@trigger_error('The ' . __NAMESPACE__ . ' \DedupeEntity is deprecated in
 | 
			
		||||
Drupal 8.4.x and will be removed before Drupal 9.0.0. Instead, use ' . __NAMESPACE__ . ' \MakeUniqueEntityField', E_USER_DEPRECATED);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Ensures value is not duplicated against an entity field.
 | 
			
		||||
| 
						 | 
				
			
			@ -18,68 +16,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		|||
 * @MigrateProcessPlugin(
 | 
			
		||||
 *   id = "dedupe_entity"
 | 
			
		||||
 * )
 | 
			
		||||
 */
 | 
			
		||||
class DedupeEntity extends DedupeBase implements ContainerFactoryPluginInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The entity storage.
 | 
			
		||||
 *
 | 
			
		||||
   * @var \Drupal\Core\Entity\EntityStorageInterface
 | 
			
		||||
 * @deprecated in Drupal 8.4.x and will be removed in Drupal 9.0.x. Use
 | 
			
		||||
 *   \Drupal\migrate\Plugin\migrate\process\MakeUniqueEntityField instead.
 | 
			
		||||
 */
 | 
			
		||||
  protected $entityStorage;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The current migration.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\migrate\Plugin\MigrationInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $migration;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager) {
 | 
			
		||||
    parent::__construct($configuration, $plugin_id, $plugin_definition);
 | 
			
		||||
    $this->migration = $migration;
 | 
			
		||||
    $this->entityStorage = $entity_type_manager->getStorage($this->configuration['entity_type']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@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('entity_type.manager')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function exists($value) {
 | 
			
		||||
    // Plugins are cached so for every run we need a new query object.
 | 
			
		||||
    $query = $this
 | 
			
		||||
      ->entityStorage->getQuery()
 | 
			
		||||
      ->condition($this->configuration['field'], $value);
 | 
			
		||||
    if (!empty($this->configuration['migrated'])) {
 | 
			
		||||
      // Check if each entity is in the ID map.
 | 
			
		||||
      $idMap = $this->migration->getIdMap();
 | 
			
		||||
      foreach ($query->execute() as $id) {
 | 
			
		||||
        $dest_id_values[$this->configuration['field']] = $id;
 | 
			
		||||
        if ($idMap->lookupSourceID($dest_id_values)) {
 | 
			
		||||
          return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // Just check if any such entity exists.
 | 
			
		||||
      return $query->count()->execute();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
class DedupeEntity extends MakeUniqueEntityField { }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\migrate\Plugin\migrate\process;
 | 
			
		||||
 | 
			
		||||
use Drupal\migrate\ProcessPluginBase;
 | 
			
		||||
use Drupal\migrate\MigrateExecutableInterface;
 | 
			
		||||
use Drupal\migrate\Row;
 | 
			
		||||
use Drupal\migrate\MigrateException;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This plugin ensures the source value is unique.
 | 
			
		||||
 *
 | 
			
		||||
 * The MakeUniqueBase process plugin is used to avoid duplication at the
 | 
			
		||||
 * destination. For example, when creating filter format names, the source
 | 
			
		||||
 * value is checked against the existing filter format names and if it exists,
 | 
			
		||||
 * a numeric postfix is added and incremented until a unique value is created.
 | 
			
		||||
 * An optional postfix string can be insert before the numeric postfix.
 | 
			
		||||
 *
 | 
			
		||||
 * Available configuration keys
 | 
			
		||||
 *   - start: (optional) The position at which to start reading.
 | 
			
		||||
 *   - length: (optional) The number of characters to read.
 | 
			
		||||
 *   - postfix: (optional) A string to insert before the numeric postfix.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\migrate\Plugin\MigrateProcessInterface
 | 
			
		||||
 */
 | 
			
		||||
abstract class MakeUniqueBase extends ProcessPluginBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a unique value based on the source value.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $value
 | 
			
		||||
   *   The input string.
 | 
			
		||||
   * @param \Drupal\migrate\MigrateExecutableInterface $migrate_executable
 | 
			
		||||
   *   The migration in which this process is being executed.
 | 
			
		||||
   * @param \Drupal\migrate\Row $row
 | 
			
		||||
   *   The row from the source to process.
 | 
			
		||||
   * @param string $destination_property
 | 
			
		||||
   *   The destination property currently worked on. This is only used together
 | 
			
		||||
   *   with the $row above.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The unique version of the input value.
 | 
			
		||||
   *
 | 
			
		||||
   * @throws \Drupal\migrate\MigrateException
 | 
			
		||||
   */
 | 
			
		||||
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
 | 
			
		||||
    $i = 1;
 | 
			
		||||
    $postfix = isset($this->configuration['postfix']) ? $this->configuration['postfix'] : '';
 | 
			
		||||
    $start = isset($this->configuration['start']) ? $this->configuration['start'] : 0;
 | 
			
		||||
    if (!is_int($start)) {
 | 
			
		||||
      throw new MigrateException('The start position configuration key should be an integer. Omit this key to capture from the beginning of the string.');
 | 
			
		||||
    }
 | 
			
		||||
    $length = isset($this->configuration['length']) ? $this->configuration['length'] : NULL;
 | 
			
		||||
    if (!is_null($length) && !is_int($length)) {
 | 
			
		||||
      throw new MigrateException('The character length configuration key should be an integer. Omit this key to capture the entire string.');
 | 
			
		||||
    }
 | 
			
		||||
    // Use optional start or length to return a portion of the unique value.
 | 
			
		||||
    $value = Unicode::substr($value, $start, $length);
 | 
			
		||||
    $new_value = $value;
 | 
			
		||||
    while ($this->exists($new_value)) {
 | 
			
		||||
      $new_value = $value . $postfix . $i++;
 | 
			
		||||
    }
 | 
			
		||||
    return $new_value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * This is a query checking the existence of some value.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $value
 | 
			
		||||
   *   The value to check.
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if the value exists.
 | 
			
		||||
   */
 | 
			
		||||
  abstract protected function exists($value);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,149 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\migrate\Plugin\migrate\process;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 | 
			
		||||
use Drupal\migrate\Plugin\MigrationInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Ensures the source value is made unique against an entity field.
 | 
			
		||||
 *
 | 
			
		||||
 * The make_unique process plugin is typically used to make the entity id
 | 
			
		||||
 * unique, ensuring that migrated entity data is preserved.
 | 
			
		||||
 *
 | 
			
		||||
 * The make_unique process plugin has two required configuration keys,
 | 
			
		||||
 * entity_type and field. It's typically used with an entity destination, making
 | 
			
		||||
 * sure that after saving the entity, the field value is unique. For example,
 | 
			
		||||
 * if the value is foo and there is already an entity where the field value is
 | 
			
		||||
 * foo, then the plugin will return foo1.
 | 
			
		||||
 *
 | 
			
		||||
 * The optional configuration key postfix which will be added between the number
 | 
			
		||||
 * and the original value, for example, foo_1 for postfix: _. Note that the
 | 
			
		||||
 * value of postfix is ignored if the value is not changed, if it was already
 | 
			
		||||
 * unique.
 | 
			
		||||
 *
 | 
			
		||||
 * The optional configuration key migrated, if true, indicates that an entity
 | 
			
		||||
 * will only be considered a duplicate if it was migrated by the current
 | 
			
		||||
 * migration.
 | 
			
		||||
 *
 | 
			
		||||
 * Available configuration keys
 | 
			
		||||
 *   - entity_type: The entity type.
 | 
			
		||||
 *   - field: The entity field for the given value.
 | 
			
		||||
 *   - migrated: (optional) A boolean to indicate that making the field unique
 | 
			
		||||
 *     only occurs for migrated entities.
 | 
			
		||||
 *   - start: (optional) The position at which to start reading.
 | 
			
		||||
 *   - length: (optional) The number of characters to read.
 | 
			
		||||
 *   - postfix: (optional) A string to insert before the numeric postfix.
 | 
			
		||||
 *
 | 
			
		||||
 * Examples:
 | 
			
		||||
 *
 | 
			
		||||
 * @code
 | 
			
		||||
 * process:
 | 
			
		||||
 *   format:
 | 
			
		||||
 *   -
 | 
			
		||||
 *     plugin: machine_name
 | 
			
		||||
 *     source: name
 | 
			
		||||
 *   -
 | 
			
		||||
 *     plugin: make_unique_entity_field
 | 
			
		||||
 *     entity_type: filter_format
 | 
			
		||||
 *     field: format
 | 
			
		||||
 *
 | 
			
		||||
 * @endcode
 | 
			
		||||
 *
 | 
			
		||||
 * This will create a format machine name out the human readable name and make
 | 
			
		||||
 * sure it's unique.
 | 
			
		||||
 *
 | 
			
		||||
 * @code
 | 
			
		||||
 * process:
 | 
			
		||||
 *   format:
 | 
			
		||||
 *   -
 | 
			
		||||
 *     plugin: machine_name
 | 
			
		||||
 *     source: name
 | 
			
		||||
 *   -
 | 
			
		||||
 *     plugin: make_unique_entity_field
 | 
			
		||||
 *     entity_type: filter_format
 | 
			
		||||
 *     field: format
 | 
			
		||||
 *     postfix: _
 | 
			
		||||
 *     migrated: true
 | 
			
		||||
 *
 | 
			
		||||
 * @endcode
 | 
			
		||||
 *
 | 
			
		||||
 * This will create a format machine name out the human readable name and make
 | 
			
		||||
 * sure it's unique if the entity was migrated. The postfix character is
 | 
			
		||||
 * inserted between the added number and the original value.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\migrate\Plugin\migrate\process\MakeUniqueBase
 | 
			
		||||
 * @see \Drupal\migrate\Plugin\MigrateProcessInterface
 | 
			
		||||
 *
 | 
			
		||||
 * @MigrateProcessPlugin(
 | 
			
		||||
 *   id = "make_unique_entity_field"
 | 
			
		||||
 * )
 | 
			
		||||
 */
 | 
			
		||||
class MakeUniqueEntityField extends MakeUniqueBase implements ContainerFactoryPluginInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The entity type manager.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $entityTypeManager;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The current migration.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\migrate\Plugin\MigrationInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $migration;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager) {
 | 
			
		||||
    parent::__construct($configuration, $plugin_id, $plugin_definition);
 | 
			
		||||
    $this->migration = $migration;
 | 
			
		||||
    $this->entityTypeManager = $entity_type_manager;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@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('entity_type.manager')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function exists($value) {
 | 
			
		||||
    // Plugins are cached so for every run we need a new query object.
 | 
			
		||||
    $query = $this
 | 
			
		||||
      ->entityTypeManager
 | 
			
		||||
      ->getStorage($this->configuration['entity_type'])
 | 
			
		||||
      ->getQuery()
 | 
			
		||||
      ->condition($this->configuration['field'], $value);
 | 
			
		||||
    if (!empty($this->configuration['migrated'])) {
 | 
			
		||||
      // Check if each entity is in the ID map.
 | 
			
		||||
      $idMap = $this->migration->getIdMap();
 | 
			
		||||
      foreach ($query->execute() as $id) {
 | 
			
		||||
        $dest_id_values[$this->configuration['field']] = $id;
 | 
			
		||||
        if ($idMap->lookupSourceID($dest_id_values)) {
 | 
			
		||||
          return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // Just check if any such entity exists.
 | 
			
		||||
      return $query->count()->execute();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,6 +2,9 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\Tests\migrate\Unit\process;
 | 
			
		||||
 | 
			
		||||
@trigger_error('The ' . __NAMESPACE__ . '\DedupeEntityTest is deprecated in
 | 
			
		||||
Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use ' . __NAMESPACE__ . '\MakeUniqueEntityFieldTest', E_USER_DEPRECATED);
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Entity\EntityStorageInterface;
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
 | 
			
		||||
use Drupal\Core\Entity\Query\QueryInterface;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\migrate\Unit\process;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Entity\EntityStorageInterface;
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
 | 
			
		||||
use Drupal\Core\Entity\Query\QueryInterface;
 | 
			
		||||
use Drupal\migrate\Plugin\migrate\process\MakeUniqueEntityField;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\MakeUniqueEntityField
 | 
			
		||||
 * @group migrate
 | 
			
		||||
 */
 | 
			
		||||
class MakeUniqueEntityFieldTest extends MigrateProcessTestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mock entity query.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Entity\Query\QueryInterface
 | 
			
		||||
   * @var \Drupal\Core\Entity\Query\QueryFactory
 | 
			
		||||
   */
 | 
			
		||||
  protected $entityQuery;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mocked entity type manager.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
 | 
			
		||||
   */
 | 
			
		||||
  protected $entityTypeManager;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The migration configuration, initialized to set the ID to test.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $migrationConfiguration = [
 | 
			
		||||
    'id' => 'test',
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    $this->entityQuery = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryInterface')
 | 
			
		||||
      ->disableOriginalConstructor()
 | 
			
		||||
      ->getMock();
 | 
			
		||||
    $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
 | 
			
		||||
 | 
			
		||||
    $storage = $this->getMock(EntityStorageInterface::class);
 | 
			
		||||
    $storage->expects($this->any())
 | 
			
		||||
      ->method('getQuery')
 | 
			
		||||
      ->willReturn($this->entityQuery);
 | 
			
		||||
    $this->entityTypeManager->expects($this->any())
 | 
			
		||||
      ->method('getStorage')
 | 
			
		||||
      ->with('test_entity_type')
 | 
			
		||||
      ->willReturn($storage);
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests making an entity field value unique.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestMakeUniqueEntityField
 | 
			
		||||
   */
 | 
			
		||||
  public function testMakeUniqueEntityField($count, $postfix = '', $start = NULL, $length = NULL) {
 | 
			
		||||
    $configuration = [
 | 
			
		||||
      'entity_type' => 'test_entity_type',
 | 
			
		||||
      'field' => 'test_field',
 | 
			
		||||
    ];
 | 
			
		||||
    if ($postfix) {
 | 
			
		||||
      $configuration['postfix'] = $postfix;
 | 
			
		||||
    }
 | 
			
		||||
    $configuration['start'] = isset($start) ? $start : NULL;
 | 
			
		||||
    $configuration['length'] = isset($length) ? $length : NULL;
 | 
			
		||||
    $plugin = new MakeUniqueEntityField($configuration, 'make_unique', [], $this->getMigration(), $this->entityTypeManager);
 | 
			
		||||
    $this->entityQueryExpects($count);
 | 
			
		||||
    $value = $this->randomMachineName(32);
 | 
			
		||||
    $actual = $plugin->transform($value, $this->migrateExecutable, $this->row, 'testproperty');
 | 
			
		||||
    $expected = Unicode::substr($value, $start, $length);
 | 
			
		||||
    $expected .= $count ? $postfix . $count : '';
 | 
			
		||||
    $this->assertSame($expected, $actual);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that invalid start position throws an exception.
 | 
			
		||||
   */
 | 
			
		||||
  public function testMakeUniqueEntityFieldEntityInvalidStart() {
 | 
			
		||||
    $configuration = [
 | 
			
		||||
      'entity_type' => 'test_entity_type',
 | 
			
		||||
      'field' => 'test_field',
 | 
			
		||||
      'start' => 'foobar',
 | 
			
		||||
    ];
 | 
			
		||||
    $plugin = new MakeUniqueEntityField($configuration, 'make_unique', [], $this->getMigration(), $this->entityTypeManager);
 | 
			
		||||
    $this->setExpectedException('Drupal\migrate\MigrateException', 'The start position configuration key should be an integer. Omit this key to capture from the beginning of the string.');
 | 
			
		||||
    $plugin->transform('test_start', $this->migrateExecutable, $this->row, 'testproperty');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that invalid length option throws an exception.
 | 
			
		||||
   */
 | 
			
		||||
  public function testMakeUniqueEntityFieldEntityInvalidLength() {
 | 
			
		||||
    $configuration = [
 | 
			
		||||
      'entity_type' => 'test_entity_type',
 | 
			
		||||
      'field' => 'test_field',
 | 
			
		||||
      'length' => 'foobar',
 | 
			
		||||
    ];
 | 
			
		||||
    $plugin = new MakeUniqueEntityField($configuration, 'make_unique', [], $this->getMigration(), $this->entityTypeManager);
 | 
			
		||||
    $this->setExpectedException('Drupal\migrate\MigrateException', 'The character length configuration key should be an integer. Omit this key to capture the entire string.');
 | 
			
		||||
    $plugin->transform('test_length', $this->migrateExecutable, $this->row, 'testproperty');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testMakeUniqueEntityField().
 | 
			
		||||
   */
 | 
			
		||||
  public function providerTestMakeUniqueEntityField() {
 | 
			
		||||
    return [
 | 
			
		||||
      // Tests no duplication.
 | 
			
		||||
      [0],
 | 
			
		||||
      // Tests no duplication and start position.
 | 
			
		||||
      [0, NULL, 10],
 | 
			
		||||
      // Tests no duplication, start position, and length.
 | 
			
		||||
      [0, NULL, 5, 10],
 | 
			
		||||
      // Tests no duplication and length.
 | 
			
		||||
      [0, NULL, NULL, 10],
 | 
			
		||||
      // Tests duplication.
 | 
			
		||||
      [3],
 | 
			
		||||
      // Tests duplication and start position.
 | 
			
		||||
      [3, NULL, 10],
 | 
			
		||||
      // Tests duplication, start position, and length.
 | 
			
		||||
      [3, NULL, 5, 10],
 | 
			
		||||
      // Tests duplication and length.
 | 
			
		||||
      [3, NULL, NULL, 10],
 | 
			
		||||
      // Tests no duplication and postfix.
 | 
			
		||||
      [0, '_'],
 | 
			
		||||
      // Tests no duplication, postfix, and start position.
 | 
			
		||||
      [0, '_', 5],
 | 
			
		||||
      // Tests no duplication, postfix, start position, and length.
 | 
			
		||||
      [0, '_', 5, 10],
 | 
			
		||||
      // Tests no duplication, postfix, and length.
 | 
			
		||||
      [0, '_', NULL, 10],
 | 
			
		||||
      // Tests duplication and postfix.
 | 
			
		||||
      [2, '_'],
 | 
			
		||||
      // Tests duplication, postfix, and start position.
 | 
			
		||||
      [2, '_', 5],
 | 
			
		||||
      // Tests duplication, postfix, start position, and length.
 | 
			
		||||
      [2, '_', 5, 10],
 | 
			
		||||
      // Tests duplication, postfix, and length.
 | 
			
		||||
      [2, '_', NULL, 10],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to add expectations to the mock entity query object.
 | 
			
		||||
   *
 | 
			
		||||
   * @param int $count
 | 
			
		||||
   *   The number of unique values to be set up.
 | 
			
		||||
   */
 | 
			
		||||
  protected function entityQueryExpects($count) {
 | 
			
		||||
    $this->entityQuery->expects($this->exactly($count + 1))
 | 
			
		||||
      ->method('condition')
 | 
			
		||||
      ->will($this->returnValue($this->entityQuery));
 | 
			
		||||
    $this->entityQuery->expects($this->exactly($count + 1))
 | 
			
		||||
      ->method('count')
 | 
			
		||||
      ->will($this->returnValue($this->entityQuery));
 | 
			
		||||
    $this->entityQuery->expects($this->exactly($count + 1))
 | 
			
		||||
      ->method('execute')
 | 
			
		||||
      ->will($this->returnCallback(function () use (&$count) { return $count--;}));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests making an entity field value unique only for migrated entities.
 | 
			
		||||
   */
 | 
			
		||||
  public function testMakeUniqueEntityFieldMigrated() {
 | 
			
		||||
    $configuration = [
 | 
			
		||||
      'entity_type' => 'test_entity_type',
 | 
			
		||||
      'field' => 'test_field',
 | 
			
		||||
      'migrated' => TRUE,
 | 
			
		||||
    ];
 | 
			
		||||
    $plugin = new MakeUniqueEntityField($configuration, 'make_unique', [], $this->getMigration(), $this->entityTypeManager);
 | 
			
		||||
 | 
			
		||||
    // Setup the entityQuery used in MakeUniqueEntityFieldEntity::exists. The
 | 
			
		||||
    // map, $map, is an array consisting of the four input parameters to the
 | 
			
		||||
    // query condition method and then the query to return. Both 'forum' and
 | 
			
		||||
    // 'test_vocab' are existing entities. There is no 'test_vocab1'.
 | 
			
		||||
    $map = [];
 | 
			
		||||
    foreach (['forums', 'test_vocab', 'test_vocab1'] as $id) {
 | 
			
		||||
      $query = $this->prophesize(QueryInterface::class);
 | 
			
		||||
      $query->willBeConstructedWith([]);
 | 
			
		||||
      $query->execute()->willReturn($id === 'test_vocab1' ? [] : [$id]);
 | 
			
		||||
      $map[] = ['test_field', $id, NULL, NULL, $query->reveal()];
 | 
			
		||||
    }
 | 
			
		||||
    $this->entityQuery
 | 
			
		||||
      ->method('condition')
 | 
			
		||||
      ->will($this->returnValueMap($map));
 | 
			
		||||
 | 
			
		||||
    // Entity 'forums' is pre-existing, entity 'test_vocab' was migrated.
 | 
			
		||||
    $this->idMap
 | 
			
		||||
      ->method('lookupSourceID')
 | 
			
		||||
      ->will($this->returnValueMap([
 | 
			
		||||
        [['test_field' => 'forums'], FALSE],
 | 
			
		||||
        [['test_field' => 'test_vocab'], ['source_id' => 42]],
 | 
			
		||||
      ]));
 | 
			
		||||
 | 
			
		||||
    // Existing entity 'forums' was not migrated, value should not be unique.
 | 
			
		||||
    $actual = $plugin->transform('forums', $this->migrateExecutable, $this->row, 'testproperty');
 | 
			
		||||
    $this->assertEquals('forums', $actual, 'Pre-existing name is re-used');
 | 
			
		||||
 | 
			
		||||
    // Entity 'test_vocab' was migrated, value should be unique.
 | 
			
		||||
    $actual = $plugin->transform('test_vocab', $this->migrateExecutable, $this->row, 'testproperty');
 | 
			
		||||
    $this->assertEquals('test_vocab1', $actual, 'Migrated name is deduplicated');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ process:
 | 
			
		|||
      plugin: machine_name
 | 
			
		||||
      source: name
 | 
			
		||||
    -
 | 
			
		||||
      plugin: dedupe_entity
 | 
			
		||||
      plugin: make_unique_entity_field
 | 
			
		||||
      entity_type: taxonomy_vocabulary
 | 
			
		||||
      field: vid
 | 
			
		||||
      length: 32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ process:
 | 
			
		|||
      plugin: machine_name
 | 
			
		||||
      source: name
 | 
			
		||||
    -
 | 
			
		||||
      plugin: dedupe_entity
 | 
			
		||||
      plugin: make_unique_entity_field
 | 
			
		||||
      entity_type: taxonomy_vocabulary
 | 
			
		||||
      field: vid
 | 
			
		||||
      length: 32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue