Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Drupal 8 Content Entity Migration Tool

Content Entity Migration Tool

This is a too programmer could use when there is a need to:

  • move taxonomy terms from one dictionary to another
  • move node from one type to another (not yet finished)

Inspired by article Changing the type of a node in Joachim's blog.

Example usage

Migration

N.B. If you move only some terms from dictionary to another, it could ruin parent/child hierachy, if parent is in one dictionary and child is in another.

N.B. If you move term outside of dictionary, term was referenced in existing field in existing node and that field was limited to "old dictionary" only, you will get unexpected results for node saving and visibility.

Move all taxonomy terms from one dictionary to another

Moves all terms from "content_keyword" dictionary to "tags" dictionary

$migrate = TermMigrationToolContent::migrateToAnotherGroup(
    'content_keyword',
    'tags'
);

Move specific taxonomy terms from one dictionary to another

Moves terms from "content_keyword" dictionary to "tags" dictionary that match ID "40", "41" or "42"

$entityIds = [
  40,
  41,
  45
];
$migrate = TermMigrationToolContent::migrateToAnotherGroup(
    'content_keyword',
    'tags'
    'content_keyword',
    function(\Drupal\Core\Entity\Query\QueryInterface $query) use($entityIds) {
      return $query->condition(TermMigrationToolContent::ENTITY_ID_COL, $entityIds, 'IN');
    }
);

Query

Query all taxonomy term IDs in specific dictionary

Returns array with IDs of terms from "content_keyword" dictionary

$migrate = TermMigrationToolContent::entitiesByGroup(
    'content_keyword'
);

Query specific taxonomy term IDs in specific dictionary

Returns array with IDs of terms from "content_keyword" dictionary that match ID "40", "41" or "42"

$entityIds = [
  40,
  41,
  45
];
$migrate = TermMigrationToolContent::entitiesByGroup(
    'content_keyword',
    function(\Drupal\Core\Entity\Query\QueryInterface $query) use($entityIds) {
      return $query->condition(TermMigrationToolContent::ENTITY_ID_COL, $entityIds, 'IN');
    }
);

Loading objects

Load taxonomy term by ID

/**
 * @var \Drupal\taxonomy\TermInterface|null $entity
 */
$entity = TermMigrationToolContent::entityById($entityId);
<?php
/**
* @property integer $total Total entities in set
* @property array $unchanged entities that were left unchanged
* @property array $succeeded entities that was successfully migrated
* @property array $failed entities that failed to migrate
* @property bool $success if all the entities were successfully migrated
*/
class ContentEntityMigrationResult extends stdClass
{}
abstract class ContentEntityMigrationTool
{
const ENTITY_TYPE = '';
const ENTITY_ID_COL = '';
const ENTITY_GROUP_COL = '';
const DATABASE_TARGET = 'default';
const DATABASE_KEY = null;
/**
* @return \Drupal\Core\Database\Connection
*/
public static function getDatabase()
{
return \Drupal\Core\Database\Database::getConnection(
static::DATABASE_TARGET,
static::DATABASE_KEY
);
}
/**
* @return \Drupal\Core\Entity\EntityStorageInterface|null
*/
public static function getStorage()
{
try {
return \Drupal
::entityTypeManager()
->getStorage(static::ENTITY_TYPE);
} catch (\Drupal\Component\Plugin\Exception\PluginNotFoundException $e) {
} catch (\Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException $e) {
}
return null;
}
/**
* @param string|int $groupId
* @param Closure|null $queryCallback
*
* @return \Drupal\Core\Entity\ContentEntityInterface[]|string[]|int[]
*/
public static function entitiesByGroup($groupId, $queryCallback = null)
{
$query = static::getStorage()->getQuery();
$query->condition(static::ENTITY_GROUP_COL, $groupId);
if ($queryCallback instanceof \Closure) {
$query = call_user_func($queryCallback, $query);
}
return $query->execute();
}
/**
* @param \Drupal\Core\Entity\ContentEntityInterface|object|string|int $entity
*
* @return int|string
*/
public static function ensureEntityId(&$entity)
{
if (is_object($entity)) {
return $entity->id();
}
return $entity;
}
/**
* @param string|int $id
*
* @return \Drupal\Core\Entity\EntityInterface|null
*/
public static function entityById($id)
{
return static::getStorage()->load($id);
}
/**
* @param \Drupal\Core\Entity\ContentEntityInterface|string|int $entity
* @param string|int $targetGroupId
*
* @return null|bool Returns boolean about success of change, null if change was not necessary
*/
abstract public static function changeEntityGroupId(&$entity, $targetGroupId);
/**
* @param $sourceGroupId
* @param $targetGroupId
* @param Closure|null $queryCallback
*
* @return ContentEntityMigrationResult
*/
public static function migrateToAnotherGroup($sourceGroupId, $targetGroupId, $queryCallback = null)
{
$entities = static::entitiesByGroup($sourceGroupId, $queryCallback);
$result = new ContentEntityMigrationResult;
$result->total = count($entities);
$result->unchanged = [];
$result->succeeded = [];
$result->failed = [];
foreach ($entities as $entity) {
$change = static::changeEntityGroupId($entity, $targetGroupId);
if (is_null($change)) {
$result->unchanged[] = $entity;
} else {
if ($change) {
$result->succeeded[] = $entity;
} else {
$result->failed[] = $entity;
}
}
}
$result->success = (count($result->succeeded) == $result->total);
return $result;
}
}
/**
* Class NodeMigrationTool
*
* @method static \Drupal\node\NodeStorage|null getStorage()
*/
//class NodeMigrationToolContent extends ContentEntityMigrationTool
//{
// const ENTITY_TYPE = 'node';
// const ENTITY_ID_COL = 'nid';
// const ENTITY_GROUP_COL = 'type';
// public static function changeEntityGroupId(&$entity, $targetGroupId, $queryCallback = null, $verify = false)
// {
// $storage = static::getStorage();
//
// // Get the names of the base tables.
// $baseTableNames = [];
// $baseTableNames[] = $storage->getBaseTable();
// $baseTableNames[] = $storage->getDataTable();
// // (Note that revision base tables don't have the bundle.)
//
// // For field tables, we need to ask the table mapping handler:
// $tableMapping = $storage->getTableMapping();
//
// // Get the names of the field tables for fields on the taxonomy dictionary.
// $fieldTableNames = [];
// foreach ($source_bundle_fields as $field) {
// $field_table = $tableMapping->getFieldTableName($field->getName());
// $fieldTableNames[] = $field_table;
//
// $field_storage_definition = $field->getFieldStorageDefinition();
// $field_revision_table = $tableMapping
// ->getDedicatedRevisionTableName($field_storage_definition);
//
// // Field revision tables DO have the bundle!
// $fieldTableNames[] = $field_revision_table;
// }
//
// // Get the node IDs to update.
// $query = \Drupal::service('entity.query')->get('node');
// // Your conditions here!
// // In our case, page nodes with a certain field populated.
// $query->condition('type', 'page');
// $query->exists(‘field_in_question’);
// $nids = $query->execute();
//
// // Base tables have 'nid' and 'type' columns.
// foreach ($baseTableNames as $table_name) {
// $query = static::getDatabase()
// ->update($table_name)
// ->fields(['type' => 'service'])
// ->condition('nid', $service_nids, 'IN')
// ->execute();
// }
//
// // Field tables have 'entity_id' and 'bundle' columns.
// foreach ($fieldTableNames as $table_name) {
// $query = static::getDatabase()
// ->update($table_name)
// ->fields(['bundle' => 'service'])
// ->condition('entity_id', $service_nids, 'IN')
// ->execute();
// }
// }
//}
/**
* Class TermMigrationTool
*
* @method static \Drupal\taxonomy\TermStorage|null getStorage()
* @method static \Drupal\taxonomy\TermInterface|null entityById($id)
*/
class TermMigrationToolContent extends ContentEntityMigrationTool
{
const ENTITY_TYPE = 'taxonomy_term';
const ENTITY_ID_COL = 'tid';
const ENTITY_GROUP_COL = 'vid';
/**
* @inheritdoc
*/
public static function changeEntityGroupId(&$entity, $targetGroupId, $queryCallback = null, $verify = false)
{
$result = null;
if (!is_object($entity)) {
$entity = static::entityById($entity);
}
if ($entity->get(static::ENTITY_GROUP_COL) === $targetGroupId) {
return $result;
}
$entityId = static::ensureEntityId($entity);
$storage = static::getStorage();
// Get the names of the base tables.
$baseTableNames = [];
$baseTableNames[] = $storage->getBaseTable();
$baseTableNames[] = $storage->getDataTable();
$transactionId = 'changeEntityGroupId';
static::getDatabase()->startTransaction($transactionId);
foreach ($baseTableNames as $tableName) {
$query = static
::getDatabase()
->update($tableName)
->fields([static::ENTITY_GROUP_COL => $targetGroupId])
->condition(static::ENTITY_ID_COL, $entityId, '=');
try {
$queryResult = $query->execute();
} catch (Exception $e) {
$queryResult = false;
}
if ($queryResult) {
if ($verify) {
$entity = static::getStorage()->load($entity);
$result = ($entity->get(static::ENTITY_GROUP_COL) === $targetGroupId);
} else {
$result = true;
}
}
if ($result === false) {
break;
}
}
if ($result === false) {
static::getDatabase()->rollBack($transactionId);
}
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.