From 1af6f9fc8bbce4d408e6f14e14803f9cb634bb05 Mon Sep 17 00:00:00 2001
From: Mike Ryan <mike.ryan@acquia.com>
Date: Mon, 10 Aug 2015 15:55:58 -0500
Subject: [PATCH] Issue #2432981: Implemented --update option for
 migrate-import command

---
 migrate_tools/migrate_tools.drush.inc   |  6 +++
 migrate_tools/src/MigrateExecutable.php | 64 +++++++++++++++++++++----
 2 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/migrate_tools/migrate_tools.drush.inc b/migrate_tools/migrate_tools.drush.inc
index 554b7389..9d62c4e0 100644
--- a/migrate_tools/migrate_tools.drush.inc
+++ b/migrate_tools/migrate_tools.drush.inc
@@ -40,6 +40,7 @@ function migrate_tools_drush_command() {
       'all' => 'Process all migrations.',
       'group' => 'Name of the migration group to import',
       'feedback' => 'Frequency of progress messages, in items processed',
+      'update' => ' In addition to processing unprocessed items from the source, update previously-imported items with the current data',
     ),
     'arguments' => array(
       'migration' => 'Name of migration(s) to import. Delimit multiple using commas.',
@@ -162,6 +163,8 @@ function drush_migrate_tools_migrate_import($migration_names = '') {
     $options['feedback'] = drush_get_option('feedback');
   }
 
+  $update = drush_get_option('update');
+
   $log = new DrushLogMigrateMessage();
 
   $migrations = drush_migrate_tools_migration_list($group_name, $migration_names);
@@ -169,6 +172,9 @@ function drush_migrate_tools_migrate_import($migration_names = '') {
   // Take it one group at a time, importing the migrations within each group.
   foreach ($migrations as $group_id => $migration_list) {
     foreach ($migration_list as $migration_id => $migration) {
+      if ($update) {
+        $migration->getIdMap()->prepareUpdate();
+      }
       $executable = new MigrateExecutable($migration, $log, $options);
       // drush_op() provides --simulate support.
       drush_op(array($executable, 'import'));
diff --git a/migrate_tools/src/MigrateExecutable.php b/migrate_tools/src/MigrateExecutable.php
index 94f24575..964109df 100644
--- a/migrate_tools/src/MigrateExecutable.php
+++ b/migrate_tools/src/MigrateExecutable.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\migrate_tools;
 
+use Drupal\migrate\Event\MigratePreRowSaveEvent;
 use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
 use Drupal\migrate\MigrateMessageInterface;
 use Drupal\migrate\Entity\MigrationInterface;
@@ -60,6 +61,13 @@ class MigrateExecutable extends MigrateExecutableBase {
    */
   protected $counter = 0;
 
+  /**
+   * Whether the destination item exists before saving.
+   *
+   * @var bool
+   */
+  protected $preExistingItem = FALSE;
+
   /**
    * {@inheritdoc}
    */
@@ -74,8 +82,10 @@ class MigrateExecutable extends MigrateExecutableBase {
       array($this, 'onMapDelete'));
     \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_IMPORT,
       array($this, 'onPostImport'));
+    \Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_ROW_SAVE,
+      array($this, 'onPreRowSave'));
     \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE,
-      array($this, 'onPostSave'));
+      array($this, 'onPostRowSave'));
   }
 
   /**
@@ -86,7 +96,14 @@ class MigrateExecutable extends MigrateExecutableBase {
    */
   public function onMapSave(MigrateMapSaveEvent $event) {
     $fields = $event->getFields();
-    $this->saveCounters[$fields['source_row_status']]++;
+    // Distinguish between creation and update.
+    if ($fields['source_row_status'] == MigrateIdMapInterface::STATUS_IMPORTED &&
+        $this->preExistingItem) {
+      $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE]++;
+    }
+    else {
+      $this->saveCounters[$fields['source_row_status']]++;
+    }
   }
 
   /**
@@ -100,14 +117,23 @@ class MigrateExecutable extends MigrateExecutableBase {
   }
 
   /**
-   * Return the number of items imported.
+   * Return the number of items created.
    *
    * @return int
    */
-  public function getImportedCount() {
+  public function getCreatedCount() {
     return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED];
   }
 
+  /**
+   * Return the number of items updated.
+   *
+   * @return int
+   */
+  public function getUpdatedCount() {
+    return $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE];
+  }
+
   /**
    * Return the number of items ignored.
    *
@@ -135,6 +161,7 @@ class MigrateExecutable extends MigrateExecutableBase {
    */
   public function getProcessedCount() {
     return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED] +
+      $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE] +
       $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED] +
       $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
   }
@@ -179,29 +206,46 @@ class MigrateExecutable extends MigrateExecutableBase {
   protected function progressMessage($done = TRUE) {
     $processed = $this->getProcessedCount();
     if ($done) {
-      $singular_message = "Processed 1 item (!successes successfully, !failures failed, !ignored ignored) - done with '!name'";
-      $plural_message = "Processed !numitems items (!successes successfully, !failures failed, !ignored ignored) - done with '!name'";
+      $singular_message = "Processed 1 item (!created created, !updated updated, !failures failed, !ignored ignored) - done with '!name'";
+      $plural_message = "Processed !numitems items (!created created, !updated updated, !failures failed, !ignored ignored) - done with '!name'";
     }
     else {
-      $singular_message = "Processed 1 item (!successes successfully, !failures failed, !ignored ignored) - continuing with '!name'";
-      $plural_message = "Processed !numitems items (!successes successfully, !failures failed, !ignored ignored) - continuing with '!name'";
+      $singular_message = "Processed 1 item (!created created, !updated updated, !failures failed, !ignored ignored) - continuing with '!name'";
+      $plural_message = "Processed !numitems items (!created created, !updated updated, !failures failed, !ignored ignored) - continuing with '!name'";
     }
     $this->message->display(\Drupal::translation()->formatPlural($processed,
       $singular_message, $plural_message,
         array('!numitems' => $processed,
-              '!successes' => $this->getImportedCount(),
+              '!created' => $this->getCreatedCount(),
+              '!updated' => $this->getUpdatedCount(),
               '!failures' => $this->getFailedCount(),
               '!ignored' => $this->getIgnoredCount(),
               '!name' => $this->migration->id())));
   }
 
+  /**
+   * React to an item about to be imported.
+   *
+   * @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
+   *   The pre-save event.
+   */
+  public function onPreRowSave(MigratePreRowSaveEvent $event) {
+    $id_map = $event->getRow()->getIdMap();
+    if (!empty($id_map['destid1'])) {
+      $this->preExistingItem = TRUE;
+    }
+    else {
+      $this->preExistingItem = FALSE;
+    }
+  }
+
   /**
    * React to item import.
    *
    * @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event
    *   The post-save event.
    */
-  public function onPostSave(MigratePostRowSaveEvent $event) {
+  public function onPostRowSave(MigratePostRowSaveEvent $event) {
     if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
       $this->progressMessage(FALSE);
       $this->resetCounters();
-- 
GitLab