/** * Returns the source. * * Makes sure source is initialized based on migration settings. * * @return \Drupal\migrate\Plugin\MigrateSourceInterface * The source. */ protected function getSource() { if (!isset($this->source)) { $this->source = $this->migration->getSourcePlugin(); } return $this->source; }
/** * {@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(); } } }
/** * Gets the source plugin to test. * * @param array $configuration * (optional) The source configuration. Defaults to an empty array. * @param array $migrate_config * (optional) The migration configuration to be used in * parent::getMigration(). Defaults to an empty array. * @param int $status * (optional) The default status for the new rows to be imported. Defaults * to MigrateIdMapInterface::STATUS_NEEDS_UPDATE. * * @return \Drupal\migrate\Plugin\MigrateSourceInterface * A mocked source plugin. */ protected function getSource($configuration = [], $migrate_config = [], $status = MigrateIdMapInterface::STATUS_NEEDS_UPDATE) { $this->migrationConfiguration = $this->defaultMigrationConfiguration + $migrate_config; $this->migration = parent::getMigration(); $this->executable = $this->getMigrateExecutable($this->migration); // Update the idMap for Source so the default is that the row has already // been imported. This allows us to use the highwater mark to decide on the // outcome of whether we choose to import the row. $id_map_array = ['original_hash' => '', 'hash' => '', 'source_row_status' => $status]; $this->idMap->expects($this->any())->method('getRowBySource')->willReturn($id_map_array); $constructor_args = [$configuration, 'd6_action', [], $this->migration]; $methods = ['getModuleHandler', 'fields', 'getIds', '__toString', 'getIterator', 'prepareRow', 'initializeIterator', 'calculateDependencies']; $source_plugin = $this->getMock('\\Drupal\\migrate\\Plugin\\migrate\\source\\SourcePluginBase', $methods, $constructor_args); $source_plugin->expects($this->any())->method('fields')->willReturn([]); $source_plugin->expects($this->any())->method('getIds')->willReturn([]); $source_plugin->expects($this->any())->method('__toString')->willReturn(''); $source_plugin->expects($this->any())->method('prepareRow')->willReturn(empty($migrate_config['prepare_row_false'])); $source_plugin->expects($this->any())->method('initializeIterator')->willReturn([]); $iterator = new \ArrayIterator([$this->row]); $source_plugin->expects($this->any())->method('getIterator')->willReturn($iterator); $module_handler = $this->getMock('\\Drupal\\Core\\Extension\\ModuleHandlerInterface'); $source_plugin->expects($this->any())->method('getModuleHandler')->willReturn($module_handler); $this->migration->expects($this->any())->method('getSourcePlugin')->willReturn($source_plugin); return $this->migration->getSourcePlugin(); }
/** * Create the map and message tables if they don't already exist. */ protected function ensureTables() { if (!$this->getDatabase()->schema()->tableExists($this->mapTableName)) { // Generate appropriate schema info for the map and message tables, // and map from the source field names to the map/msg field names. $count = 1; $source_id_schema = array(); foreach ($this->migration->getSourcePlugin()->getIds() as $id_definition) { $mapkey = 'sourceid' . $count++; $source_id_schema[$mapkey] = $this->getFieldSchema($id_definition); $source_id_schema[$mapkey]['not null'] = TRUE; } $source_ids_hash[static::SOURCE_IDS_HASH] = array('type' => 'varchar', 'length' => '64', 'not null' => TRUE, 'description' => 'Hash of source ids. Used as primary key'); $fields = $source_ids_hash + $source_id_schema; // Add destination identifiers to map table. // @todo How do we discover the destination schema? $count = 1; foreach ($this->migration->getDestinationPlugin()->getIds() as $id_definition) { // Allow dest identifier fields to be NULL (for IGNORED/FAILED cases). $mapkey = 'destid' . $count++; $fields[$mapkey] = $this->getFieldSchema($id_definition); $fields[$mapkey]['not null'] = FALSE; } $fields['source_row_status'] = array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => MigrateIdMapInterface::STATUS_IMPORTED, 'description' => 'Indicates current status of the source row'); $fields['rollback_action'] = array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => MigrateIdMapInterface::ROLLBACK_DELETE, 'description' => 'Flag indicating what to do for this item on rollback'); $fields['last_imported'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'UNIX timestamp of the last time this row was imported'); $fields['hash'] = array('type' => 'varchar', 'length' => '64', 'not null' => FALSE, 'description' => 'Hash of source row data, for detecting changes'); $schema = array('description' => 'Mappings from source identifier value(s) to destination identifier value(s).', 'fields' => $fields, 'primary key' => array(static::SOURCE_IDS_HASH)); $this->getDatabase()->schema()->createTable($this->mapTableName, $schema); // Now do the message table. if (!$this->getDatabase()->schema()->tableExists($this->messageTableName())) { $fields = array(); $fields['msgid'] = array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE); $fields += $source_ids_hash; $fields['level'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1); $fields['message'] = array('type' => 'text', 'size' => 'medium', 'not null' => TRUE); $schema = array('description' => 'Messages generated during a migration process', 'fields' => $fields, 'primary key' => array('msgid')); $this->getDatabase()->schema()->createTable($this->messageTableName(), $schema); } } else { // Add any missing columns to the map table. if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, 'rollback_action')) { $this->getDatabase()->schema()->addField($this->mapTableName, 'rollback_action', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Flag indicating what to do for this item on rollback')); } if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, 'hash')) { $this->getDatabase()->schema()->addField($this->mapTableName, 'hash', array('type' => 'varchar', 'length' => '64', 'not null' => FALSE, 'description' => 'Hash of source row data, for detecting changes')); } if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, static::SOURCE_IDS_HASH)) { $this->getDatabase()->schema()->addField($this->mapTableName, static::SOURCE_IDS_HASH, array('type' => 'varchar', 'length' => '64', 'not null' => TRUE, 'description' => 'Hash of source ids. Used as primary key')); } } }
/** * Instantiates the source plugin under test. * * @param array $configuration * The source plugin configuration. * * @return \Drupal\migrate\Plugin\MigrateSourceInterface|object * The fully configured source plugin. */ protected function getPlugin(array $configuration) { // Only create the plugin once per test. if ($this->plugin) { return $this->plugin; } $class = ltrim($this->getPluginClass(), '\\'); /** @var \Drupal\migrate\Plugin\MigratePluginManager $plugin_manager */ $plugin_manager = $this->container->get('plugin.manager.migrate.source'); foreach ($plugin_manager->getDefinitions() as $id => $definition) { if (ltrim($definition['class'], '\\') == $class) { $this->plugin = $plugin_manager->createInstance($id, $configuration, $this->migration->reveal()); $this->migration->getSourcePlugin()->willReturn($this->plugin); return $this->plugin; } } $this->fail('No plugin found for class ' . $class); }