/**
  * {@inheritdoc}
  *
  * The migration iterates over rows returned by the source plugin. This
  * method determines the next row which will be processed and imported into
  * the system.
  *
  * The method tracks the source and destination IDs using the ID map plugin.
  *
  * This also takes care about highwater support. Highwater allows to reimport
  * rows from a previous migration run, which got changed in the meantime.
  * This is done by specifying a highwater field, which is compared with the
  * last time, the migration got executed (originalHighWater).
  */
 public function next()
 {
     $this->currentSourceIds = NULL;
     $this->currentRow = NULL;
     // In order to find the next row we want to process, we ask the source
     // plugin for the next possible row.
     while (!isset($this->currentRow) && $this->getIterator()->valid()) {
         $row_data = $this->getIterator()->current() + $this->configuration;
         $this->getIterator()->next();
         $row = new Row($row_data, $this->migration->getSourcePlugin()->getIds(), $this->migration->get('destinationIds'));
         // Populate the source key for this row.
         $this->currentSourceIds = $row->getSourceIdValues();
         // Pick up the existing map row, if any, unless getNextRow() did it.
         if (!$this->mapRowAdded && ($id_map = $this->idMap->getRowBySource($this->currentSourceIds))) {
             $row->setIdMap($id_map);
         }
         // Clear any previous messages for this row before potentially adding
         // new ones.
         if (!empty($this->currentSourceIds)) {
             $this->idMap->delete($this->currentSourceIds, TRUE);
         }
         // Preparing the row gives source plugins the chance to skip.
         if ($this->prepareRow($row) === FALSE) {
             continue;
         }
         // Check whether the row needs processing.
         // 1. This row has not been imported yet.
         // 2. Explicitly set to update.
         // 3. The row is newer than the current highwater mark.
         // 4. If no such property exists then try by checking the hash of the row.
         if (!$row->getIdMap() || $row->needsUpdate() || $this->aboveHighwater($row) || $this->rowChanged($row)) {
             $this->currentRow = $row->freezeSource();
         }
     }
 }
Example #2
0
 /**
  * {@inheritdoc}
  */
 public function saveIdMapping(Row $row, array $destination_id_values, $source_row_status = MigrateIdMapInterface::STATUS_IMPORTED, $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE)
 {
     // Construct the source key.
     $source_id_values = $row->getSourceIdValues();
     // Construct the source key and initialize to empty variable keys.
     $fields = [];
     foreach ($this->sourceIdFields() as $field_name => $key_name) {
         // A NULL key value is usually an indication of a problem.
         if (!isset($source_id_values[$field_name])) {
             $this->message->display($this->t('Did not save to map table due to NULL value for key field @field', array('@field' => $field_name)), 'error');
             return;
         }
         $fields[$key_name] = $source_id_values[$field_name];
     }
     if (!$fields) {
         return;
     }
     $fields += array('source_row_status' => (int) $source_row_status, 'rollback_action' => (int) $rollback_action, 'hash' => $row->getHash());
     $count = 0;
     foreach ($destination_id_values as $dest_id) {
         $fields['destid' . ++$count] = $dest_id;
     }
     if ($count && $count != count($this->destinationIdFields())) {
         $this->message->display(t('Could not save to map table due to missing destination id values'), 'error');
         return;
     }
     if ($this->migration->get('trackLastImported')) {
         $fields['last_imported'] = time();
     }
     $keys = [static::SOURCE_IDS_HASH => $this->getSourceIDsHash($source_id_values)];
     // Notify anyone listening of the map row we're about to save.
     $this->eventDispatcher->dispatch(MigrateEvents::MAP_SAVE, new MigrateMapSaveEvent($this, $fields));
     $this->getDatabase()->merge($this->mapTableName())->key($keys)->fields($fields)->execute();
 }
 /**
  * {@inheritdoc}
  */
 public function import()
 {
     // Only begin the import operation if the migration is currently idle.
     if ($this->migration->getStatus() !== MigrationInterface::STATUS_IDLE) {
         $this->message->display($this->t('Migration @id is busy with another operation: @status', array('@id' => $this->migration->id(), '@status' => $this->t($this->migration->getStatusLabel()))), 'error');
         return MigrationInterface::RESULT_FAILED;
     }
     $this->getEventDispatcher()->dispatch(MigrateEvents::PRE_IMPORT, new MigrateImportEvent($this->migration));
     // Knock off migration if the requirements haven't been met.
     try {
         $this->migration->checkRequirements();
     } catch (RequirementsException $e) {
         $this->message->display($this->t('Migration @id did not meet the requirements. @message @requirements', array('@id' => $this->migration->id(), '@message' => $e->getMessage(), '@requirements' => $e->getRequirementsString())), 'error');
         return MigrationInterface::RESULT_FAILED;
     }
     $this->migration->setStatus(MigrationInterface::STATUS_IMPORTING);
     $return = MigrationInterface::RESULT_COMPLETED;
     $source = $this->getSource();
     $id_map = $this->migration->getIdMap();
     try {
         $source->rewind();
     } catch (\Exception $e) {
         $this->message->display($this->t('Migration failed with source plugin exception: @e', array('@e' => $e->getMessage())), 'error');
         $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
         return MigrationInterface::RESULT_FAILED;
     }
     $destination = $this->migration->getDestinationPlugin();
     while ($source->valid()) {
         $row = $source->current();
         $this->sourceIdValues = $row->getSourceIdValues();
         try {
             $this->processRow($row);
             $save = TRUE;
         } catch (MigrateException $e) {
             $this->migration->getIdMap()->saveIdMapping($row, array(), $e->getStatus());
             $this->saveMessage($e->getMessage(), $e->getLevel());
             $save = FALSE;
         } catch (MigrateSkipRowException $e) {
             $id_map->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_IGNORED);
             $save = FALSE;
         }
         if ($save) {
             try {
                 $this->getEventDispatcher()->dispatch(MigrateEvents::PRE_ROW_SAVE, new MigratePreRowSaveEvent($this->migration, $row));
                 $destination_id_values = $destination->import($row, $id_map->lookupDestinationId($this->sourceIdValues));
                 $this->getEventDispatcher()->dispatch(MigrateEvents::POST_ROW_SAVE, new MigratePostRowSaveEvent($this->migration, $row, $destination_id_values));
                 if ($destination_id_values) {
                     // We do not save an idMap entry for config.
                     if ($destination_id_values !== TRUE) {
                         $id_map->saveIdMapping($row, $destination_id_values, $this->sourceRowStatus, $destination->rollbackAction());
                     }
                 } else {
                     $id_map->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_FAILED);
                     if (!$id_map->messageCount()) {
                         $message = $this->t('New object was not saved, no error provided');
                         $this->saveMessage($message);
                         $this->message->display($message);
                     }
                 }
             } catch (MigrateException $e) {
                 $this->migration->getIdMap()->saveIdMapping($row, array(), $e->getStatus());
                 $this->saveMessage($e->getMessage(), $e->getLevel());
             } catch (\Exception $e) {
                 $this->migration->getIdMap()->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_FAILED);
                 $this->handleException($e);
             }
         }
         if ($high_water_property = $this->migration->get('highWaterProperty')) {
             $this->migration->saveHighWater($row->getSourceProperty($high_water_property['name']));
         }
         // Reset row properties.
         unset($sourceValues, $destinationValues);
         $this->sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED;
         // Check for memory exhaustion.
         if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) {
             break;
         }
         // If anyone has requested we stop, return the requested result.
         if ($this->migration->getStatus() == MigrationInterface::STATUS_STOPPING) {
             $return = $this->migration->getInterruptionResult();
             $this->migration->clearInterruptionResult();
             break;
         }
         try {
             $source->next();
         } catch (\Exception $e) {
             $this->message->display($this->t('Migration failed with source plugin exception: @e', array('@e' => $e->getMessage())), 'error');
             $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
             return MigrationInterface::RESULT_FAILED;
         }
     }
     $this->getEventDispatcher()->dispatch(MigrateEvents::POST_IMPORT, new MigrateImportEvent($this->migration));
     $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
     return $return;
 }