August 29, 2017
How to skip row if parent entity does not exist during a Drupal 8 migration
Drupal 8 lets you perform incremental migrations and update records that have changed since their last migration. If you meanwhile delete such records in Drupal 8 a migration update may break.
We ran into this issue on updating migrated forum nodes that had since received further comments on the Drupal 6 source website. Their parent node didn't exist anymore in Drupal 8:
TypeError: Argument 1 passed to Drupal\forum\ForumIndexStorage::updateIndex()
must implement interface Drupal\node\NodeInterface, null given,
called in /core/modules/forum/forum.module
on line 272 in Drupal\forum\ForumIndexStorage->updateIndex()
(line 92 of /core/modules/forum/src/ForumIndexStorage.php)
#0 /core/modules/forum/forum.module(272):
Drupal\forum\ForumIndexStorage->updateIndex(NULL)
To prevent such errors we wrote the process plugin skip_row_if_not_exist. You can configure the entity type and its property to check for and a migration message:
<?php
namespace Drupal\migrate_custom\Plugin\migrate\process;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Drupal\migrate\MigrateSkipRowException;
/**
* Skips processing the current row when a destination value does not exist.
*
* The skip_row_if_not_exist process plugin checks whether a value exists. If the
* value exists, it is returned. Otherwise, a MigrateSkipRowException
* is thrown.
*
* Available configuration keys:
* - entity: The destination entity to check for.
* - property: The destination entity property to check for.
* - message: (optional) A message to be logged in the {migrate_message_*} table
* for this row. If not set, nothing is logged in the message table.
*
* Example:
* Do not import comments for migrated nodes that do not exist any more at the
* destination.
*
* @code
* process:
* entity_id:
* -
* plugin: migration_lookup
* migration:
* - d6_node
* source: nid
* -
* # Check if a node exists in the destination database.
* plugin: skip_row_if_not_exist
* entity: node
* property: nid
* message: 'Commented entity not found.'
* @endcode
*
* This will return the node id if it exists. Otherwise, the row will be
* skipped and the message "Commented entity not found." will be logged in the
* message table.
*
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
*
* @MigrateProcessPlugin(
* id = "skip_row_if_not_exist",
* handle_multiples = TRUE
* )
*/
class SkipRowIfNotExist extends ProcessPluginBase {
protected $entity = 'node';
protected $property = 'nid';
function __construct($configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
if (!empty($configuration['entity'])) {
$this->entity = $configuration['entity'];
}
if (!empty($configuration['property'])) {
$this->property = $configuration['property'];
}
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
$count = \Drupal::entityQuery($this->entity)
->condition($this->property, $value)
->accessCheck(FALSE)
->count()
->execute();
if (!$count) {
$message = isset($this->configuration['message']) ? $this->configuration['message'] : '';
throw new MigrateSkipRowException($message);
}
return $value;
}
}
Example
To check if the parent node of a comment exists, you would do the following:
- Copy /core/modules/comment/migration_templates/d6_comment.yml to a custom migration module.
- Replace entity_id: nid with two process plugins
- Lookup the destination nid of the parent node in the migration mapping table
- Skip the whole row if the node doesn't exist
Before (excerpt)
process:
# If you are using this file to build a custom migration consider removing
# the cid field to allow incremental migrations.
cid: cid
pid:
plugin: migration_lookup
migration: d6_comment
source: pid
entity_id: nid
After (excerpt)
process:
# If you are using this file to build a custom migration consider removing
# the cid field to allow incremental migrations.
cid: cid
pid:
plugin: migration_lookup
migration: d6_comment
source: pid
entity_id:
-
plugin: migration_lookup
migration:
- d6_node
source: nid
-
plugin: skip_row_if_not_exist
entity: node
property: nid
message: 'Commented entity not found.'
Tools
Services
Comments
Where to put the process plugin
Exactly the error I was seeing and much better than deleting the rows in the comment table which how I was initially solving the problem.
I did run into a little problem with figuring out where to put the process plugin. I eventually figured it out with the help of this article from Advomatic
Thanks again.
Add new comment