diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php
index 73e7e00..09f02b9 100644
--- a/core/modules/migrate/src/MigrateExecutable.php
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -102,10 +102,9 @@ class MigrateExecutable implements MigrateExecutableInterface {
    * @throws \Drupal\migrate\MigrateException
    */
   public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, EventDispatcherInterface $event_dispatcher = NULL) {
-    $this->migration = $migration;
     $this->message = $message;
-    $this->migration->getIdMap()->setMessage($message);
     $this->eventDispatcher = $event_dispatcher;
+    $this->setMigration($migration);
     // Record the memory limit in bytes
     $limit = trim(ini_get('memory_limit'));
     if ($limit == '-1') {
@@ -133,6 +132,14 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function setMigration(MigrationInterface $migration) {
+    $this->migration = $migration;
+    $this->migration->getIdMap()->setMessage($this->message);
+  }
+
+  /**
    * Returns the source.
    *
    * Makes sure source is initialized based on migration settings.
diff --git a/core/modules/migrate/src/MigrateExecutableInterface.php b/core/modules/migrate/src/MigrateExecutableInterface.php
index 33d5914..e1d686e 100644
--- a/core/modules/migrate/src/MigrateExecutableInterface.php
+++ b/core/modules/migrate/src/MigrateExecutableInterface.php
@@ -7,6 +7,11 @@
 interface MigrateExecutableInterface {
 
   /**
+   * Sets the current migration.
+   */
+  public function setMigration(MigrationInterface $migration);
+
+  /**
    * Performs an import operation - migrate items from source to destination.
    */
   public function import();
diff --git a/core/modules/migrate/src/Plugin/migrate/process/Migration.php b/core/modules/migrate/src/Plugin/migrate/process/Migration.php
index 8520a5e..799687d 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/Migration.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/Migration.php
@@ -3,6 +3,7 @@
 namespace Drupal\migrate\Plugin\migrate\process;
 
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\MigrateSkipRowException;
 use Drupal\migrate\MigrateSkipProcessException;
 use Drupal\migrate\Plugin\MigratePluginManagerInterface;
 use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
@@ -81,15 +82,14 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
       $value = array($value);
     }
     $this->skipOnEmpty($value);
-    $self = FALSE;
+
     /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */
+    $migrations = $this->migrationPluginManager->createInstances($migration_ids);
+
+    // Lookup the destination ids.
     $destination_ids = NULL;
     $source_id_values = array();
-    $migrations = $this->migrationPluginManager->createInstances($migration_ids);
     foreach ($migrations as $migration_id => $migration) {
-      if ($migration_id == $this->migration->id()) {
-        $self = TRUE;
-      }
       if (isset($this->configuration['source_ids'][$migration_id])) {
         $configuration = array('source' => $this->configuration['source_ids'][$migration_id]);
         $source_id_values[$migration_id] = $this->processPluginManager
@@ -105,72 +105,140 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
       }
     }
 
-    if (!$destination_ids && !empty($this->configuration['no_stub'])) {
-      return NULL;
+    // If the lookup didn't succeed, try and create a stub record.
+    if (!$destination_ids && (empty($this->configuration['no_stub']))) {
+      $destination_ids = $this->createStubRecord($source_id_values, $migrations, $migrate_executable);
     }
 
-    if (!$destination_ids && ($self || isset($this->configuration['stub_id']) || count($migrations) == 1)) {
-      // If the lookup didn't succeed, figure out which migration will do the
-      // stubbing.
-      if ($self) {
-        $migration = $this->migration;
-      }
-      elseif (isset($this->configuration['stub_id'])) {
-        $migration = $migrations[$this->configuration['stub_id']];
-      }
-      else {
-        $migration = reset($migrations);
+    if ($destination_ids) {
+      if (count($destination_ids) == 1) {
+        $destination_ids = reset($destination_ids);
       }
+    }
+    else {
+      $destination_ids = NULL;
+    }
+
+    return $destination_ids;
+  }
+
+  /**
+   * Skips the migration process entirely if the value is FALSE.
+   *
+   * @param mixed $value
+   *   The incoming value to transform.
+   *
+   * @throws \Drupal\migrate\MigrateSkipProcessException
+   */
+  protected function skipOnEmpty(array $value) {
+    if (!array_filter($value)) {
+      throw new MigrateSkipProcessException();
+    }
+  }
+
+  /**
+   * Creates a stub record.
+   *
+   * @param $value
+   * @param array $source_id_values
+   * @param array $migrations
+   * @param $context
+   * @return array|null
+   *    An array of the stub record identifier values.
+   */
+  protected function createStubRecord(array $source_id_values, array $migrations, MigrateExecutableInterface $migrate_executable) {
+    $destination_ids = NULL;
+
+    $migration = $this->getStubbingMigration($migrations);
+    if ($migration) {
+      $stub_row = $this->createStubRow($migration, $source_id_values);
+
       $destination_plugin = $migration->getDestinationPlugin(TRUE);
       // Only keep the process necessary to produce the destination ID.
       $process = $migration->getProcess();
 
-      // We already have the source ID values but need to key them for the Row
-      // constructor.
-      $source_ids = $migration->getSourcePlugin()->getIds();
-      $values = array();
-      foreach (array_keys($source_ids) as $index => $source_id) {
-        $values[$source_id] = $source_id_values[$migration->id()][$index];
+      // Do a normal migration with the stub row.
+      $migrate_executable->setMigration($migration);
+      try {
+        $migrate_executable->processRow($stub_row, $process);
+      } catch (MigrateSkipRowException $e) {
+        // Process pipeline failed.
       }
+      $migrate_executable->setMigration($this->migration);
 
-      $stub_row = new Row($values + $migration->getSourceConfiguration(), $source_ids, TRUE);
-
-      // Do a normal migration with the stub row.
-      $migrate_executable->processRow($stub_row, $process);
-      $destination_ids = array();
       try {
         $destination_ids = $destination_plugin->import($stub_row);
-      }
-      catch (\Exception $e) {
-        $migration->getIdMap()->saveMessage($stub_row->getSourceIdValues(), $e->getMessage());
+      } catch (\Exception $e) {
+        $migration->getIdMap()
+          ->saveMessage($stub_row->getSourceIdValues(), $e->getMessage());
       }
 
       if ($destination_ids) {
-        $migration->getIdMap()->saveIdMapping($stub_row, $destination_ids, MigrateIdMapInterface::STATUS_NEEDS_UPDATE);
-      }
-    }
-    if ($destination_ids) {
-      if (count($destination_ids) == 1) {
-        return reset($destination_ids);
+        $migration->getIdMap()
+          ->saveIdMapping($stub_row, $destination_ids, MigrateIdMapInterface::STATUS_NEEDS_UPDATE);
       }
       else {
-        return $destination_ids;
+        $destination_ids = NULL;
       }
     }
+
+    return $destination_ids;
   }
 
   /**
-   * Skips the migration process entirely if the value is FALSE.
+   * Obtains the migration to create a stub row.
    *
-   * @param mixed $value
+   * @param array $migrations
+   *   List of migrations in the plugin configuration.
+   * @param array $context
+   *   Array of contextual variables
+   * @param array $value
    *   The incoming value to transform.
-   *
-   * @throws \Drupal\migrate\MigrateSkipProcessException
+   * @return \Drupal\migrate\Plugin\MigrationInterface
+   *   The migration to use for the stub row.
    */
-  protected function skipOnEmpty(array $value) {
-    if (!array_filter($value)) {
-      throw new MigrateSkipProcessException();
+  protected function getStubbingMigration(array $migrations) {
+    // Prefer self in the first instance.
+    if (array_key_exists($this->migration->id(), $migrations)) {
+      $migration = $this->migration;
     }
+    elseif (isset($this->configuration['stub_id'])) {
+      $migration = $migrations[$this->configuration['stub_id']];
+    }
+    elseif (count($migrations) == 1) {
+      $migration = reset($migrations);
+    }
+    else {
+      $migration = NULL;
+    }
+
+    return $migration;
   }
 
+  /**
+   * Create a stub row source for later import as stub data.
+   *
+   * This simple wrapper of the Row constructor allows sub-classing plugins to
+   * have more control over the row.
+   *
+   * @param array $values
+   *   An array of values to add as properties on the object.
+   * @param array $source_ids
+   *   An array containing the IDs of the source using the keys as the field names.
+   *
+   * @return \Drupal\migrate\Row
+   *   The stub row.
+   */
+  protected function createStubRow(MigrationInterface $migration, array $source_id_values) {
+    // We already have the source ID values but need to key them for the Row
+    // constructor.
+    $source_ids = $migration->getSourcePlugin()->getIds();
+    $values = array();
+    foreach (array_keys($source_ids) as $index => $source_id) {
+      $values[$source_id] = $source_id_values[$migration->id()][$index];
+    }
+    $values += $migration->getSourceConfiguration();
+
+    return new Row($values, $source_ids, TRUE);
+  }
 }
