/** * {@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(); } } }
/** * {@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; }