/** * Builds a row for an entity in the entity listing. * * @param EntityInterface $migration * The entity for which to build the row. * * @return array * A render array of the table row for displaying the entity. * * @see Drupal\Core\Entity\EntityListController::render() */ public function buildRow(MigrationInterface $migration) { $row['label'] = $migration->label(); $row['machine_name'] = $migration->id(); $row['status'] = $migration->getStatusLabel(); // Derive the stats $source_plugin = $migration->getSourcePlugin(); $row['total'] = $source_plugin->count(); $map = $migration->getIdMap(); $row['imported'] = $map->importedCount(); // -1 indicates uncountable sources. if ($row['total'] == -1) { $row['total'] = $this->t('N/A'); $row['unprocessed'] = $this->t('N/A'); } else { $row['unprocessed'] = $row['total'] - $map->processedCount(); } $group = $migration->get('migration_group'); if (!$group) { $group = 'default'; } // @todo: This is most likely not a Best Practice (tm). $row['messages']['data']['#markup'] = '<a href="/admin/structure/migrate/manage/' . $group . '/migrations/' . $migration->id() . '/messages">' . $map->messageCount() . '</a>'; $migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported'); $last_imported = $migrate_last_imported_store->get($migration->id(), FALSE); if ($last_imported) { /** @var DateFormatter $date_formatter */ $date_formatter = \Drupal::service('date.formatter'); $row['last_imported'] = $date_formatter->format($last_imported / 1000, 'custom', 'Y-m-d H:i:s'); } else { $row['last_imported'] = ''; } return $row; // + parent::buildRow($migration); }
/** * Test row skipping when we can't get an entity to save. * * @covers ::import * @expectedException \Drupal\migrate\MigrateException * @expectedExceptionMessage Unable to get entity */ public function testImportEntityLoadFailure() { $bundles = []; $destination = new EntityTestDestination([], '', [], $this->migration->reveal(), $this->storage->reveal(), $bundles, $this->entityManager->reveal(), $this->prophesize(FieldTypePluginManagerInterface::class)->reveal()); $destination->setEntity(FALSE); $destination->import(new Row([], [])); }
/** * @covers ::processCckFieldValues */ public function testProcessBooleanTextExplicitValues() { $info = array('widget_type' => 'optionwidgets_onoff', 'global_settings' => array('allowed_values' => "foo|Foo\nbaz|Baz")); $this->plugin->processCckFieldValues($this->migration, 'field', $info); $expected = ['value' => ['plugin' => 'static_map', 'source' => 'value', 'default_value' => 0, 'map' => ['baz' => 1]]]; $this->assertSame($expected, $this->migration->getProcess()['process']); }
/** * {@inheritdoc} */ public function processFieldFormatter(MigrationInterface $migration) { $process = []; foreach ($this->getFieldFormatterMap() as $source_format => $destination_format) { $process[0]['map'][$this->pluginId][$source_format] = $destination_format; } $migration->mergeProcessOfProperty('options/type', $process); }
/** * Allows adding data to a row before processing it. * * For example, filter module used to store filter format settings in the * variables table which now needs to be inside the filter format config * file. So, it needs to be added here. * * hook_migrate_MIGRATION_ID_prepare_row is also available. */ function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) { if ($migration->id() == 'd6_filter_formats') { $value = $source->getDatabase()->query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'mymodule_filter_foo_' . $row->getSourceProperty('format')))->fetchField(); if ($value) { $row->setSourceProperty('settings:mymodule:foo', unserialize($value)); } } }
/** * {@inheritdoc} */ public function prepareRow(Row $row) { $result_hook = $this->getModuleHandler()->invokeAll('migrate_prepare_row', array($row, $this, $this->migration)); $result_named_hook = $this->getModuleHandler()->invokeAll('migrate_' . $this->migration->id() . '_prepare_row', array($row, $this, $this->migration)); // If any of the hooks returned false, we want to skip the row. if ($result_hook && in_array(FALSE, $result_hook) || $result_named_hook && in_array(FALSE, $result_named_hook)) { return FALSE; } }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { // The data is stored differently depending on whether we're using // db storage. $value_key = $data['db_storage'] ? $field_name : "{$field_name}/value"; $format_key = $data['db_storage'] ? $field_name . '_format' : "{$field_name}/format"; $migration->setProcessOfProperty("{$field_name}/value", $value_key); // See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(), // signature_format for an example of the YAML that represents this // process array. $process = [['plugin' => 'static_map', 'bypass' => TRUE, 'source' => $format_key, 'map' => [0 => NULL]], ['plugin' => 'skip_on_empty', 'method' => 'process'], ['plugin' => 'migration', 'migration' => 'd6_filter_format', 'source' => $format_key]]; $migration->mergeProcessOfProperty("{$field_name}/format", $process); }
/** * {@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. $keys = array(); foreach ($this->sourceIdFields() as $field_name => $key_name) { // A NULL key value will fail. if (!isset($source_id_values[$field_name])) { $this->message->display(t('Could not save to map table due to NULL value for key field @field', array('@field' => $field_name)), 'error'); return; } $keys[$key_name] = $source_id_values[$field_name]; } $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(); } if ($keys) { // Notify anyone listening of the map row we're about to save. $this->eventDispatcher->dispatch(MigrateEvents::MAP_SAVE, new MigrateMapSaveEvent($this, $keys + $fields)); $this->getDatabase()->merge($this->mapTableName())->key($keys)->fields($fields)->execute(); } }
/** * {@inheritdoc} */ public function deleteBulk(array $source_id_values) { // If we have a single-column key, we can shortcut it. if (count($this->migration->getSourcePlugin()->getIds()) == 1) { $sourceids = array(); foreach ($source_id_values as $source_id) { // Notify anyone listening of the map rows we're about to delete. $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id)); $sourceids[] = $source_id; } $this->getDatabase()->delete($this->mapTableName())->condition('sourceid1', $sourceids, 'IN')->execute(); $this->getDatabase()->delete($this->messageTableName())->condition('sourceid1', $sourceids, 'IN')->execute(); } else { foreach ($source_id_values as $source_id) { // Notify anyone listening of the map rows we're deleting. $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id)); $map_query = $this->getDatabase()->delete($this->mapTableName()); $message_query = $this->getDatabase()->delete($this->messageTableName()); $count = 1; foreach ($source_id as $key_value) { $map_query->condition('sourceid' . $count, $key_value); $message_query->condition('sourceid' . $count++, $key_value); } $map_query->execute(); $message_query->execute(); } } }
/** * {@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(); } } }
/** * Tests the processRow method with an empty pipeline. */ public function testProcessRowEmptyPipeline() { $this->migration->expects($this->once())->method('getProcessPlugins')->with(NULL)->will($this->returnValue(array('test' => array()))); $row = new Row(array(), array()); $this->executable->processRow($row); $this->assertSame($row->getDestination(), array()); }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $field_info) { if ($field_info['widget_type'] == 'optionwidgets_onoff') { $process = ['value' => ['plugin' => 'static_map', 'source' => 'value', 'default_value' => 0]]; $checked_value = explode("\n", $field_info['global_settings']['allowed_values'])[1]; if (strpos($checked_value, '|') !== FALSE) { $checked_value = substr($checked_value, 0, strpos($checked_value, '|')); } $process['value']['map'][$checked_value] = 1; } else { // See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(), // signature_format for an example of the YAML that represents this // process array. $process = ['value' => 'value', 'format' => [['plugin' => 'static_map', 'bypass' => TRUE, 'source' => 'format', 'map' => [0 => NULL]], ['plugin' => 'skip_on_empty', 'method' => 'process'], ['plugin' => 'migration', 'migration' => ['d6_filter_format', 'd7_filter_format'], 'source' => 'format']]]; } $process = array('plugin' => 'iterator', 'source' => $field_name, 'process' => $process); $migration->setProcessOfProperty($field_name, $process); }
/** * Tests the walk process plugin with multiple values. */ public function testWalkWithMultipleValues() { // Set source value. $source_value = ['FOO', 'BAR', 'foobar']; // Set expected value; $expected_value = ['*Foo (mapped)*', '*Bar*', '*123*']; // Manually create the plugins. Migration::getProcessPlugins does this // normally, but the plugin system is not available. $process_plugins = $this->getProcessPlugins(); // Define the value of getProcessPlugin() for every time processRow() is // called (number of values). See the count of $source_value. foreach (range(1, count($source_value)) as $key) { $this->migration->expects($this->at($key))->method('getProcessPlugins')->will($this->returnValue($process_plugins)); } // Get migrate executable. $migrate_executable = $this->getMigrateExecutable(); // Assert the value after running the process plugins. $plugin = new Walk($this->getWalkProcessPluginConfiguration(), 'walk', []); $new_value = $plugin->transform($source_value, $migrate_executable, new Row([], []), 'test'); $this->assertSame($new_value, $expected_value); }
protected function mapJoinable() { if (!$this->getIds()) { return FALSE; } $id_map = $this->migration->getIdMap(); if (!$id_map instanceof Sql) { return FALSE; } $id_map_database_options = $id_map->getDatabase()->getConnectionOptions(); $source_database_options = $this->getDatabase()->getConnectionOptions(); foreach (array('username', 'password', 'host', 'port', 'namespace', 'driver') as $key) { if ($id_map_database_options[$key] != $source_database_options[$key]) { return FALSE; } } return TRUE; }
/** * {@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; $source_configuration = $this->migration->get('source'); // 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() + $source_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); } // In case we have specified an ID list, but the ID given by the source is // not in there, we skip the row. $id_in_the_list = $this->idList && in_array(reset($this->currentSourceIds), $this->idList); if ($this->idList && !$id_in_the_list) { continue; } // Preparing the row gives source plugins the chance to skip. if ($this->prepareRow($row) === FALSE) { continue; } // Check whether the row needs processing. // 1. Explicitly specified IDs. // 2. This row has not been imported yet. // 3. Explicitly set to update. // 4. The row is newer than the current highwater mark. // 5. If no such property exists then try by checking the hash of the row. if ($id_in_the_list || !$row->getIdMap() || $row->needsUpdate() || $this->aboveHighwater($row) || $this->rowChanged($row)) { $this->currentRow = $row->freezeSource(); } } }
/** * {@inheritdoc} */ public function deleteBulk(array $source_id_values) { // If we have a single-column key, we can shortcut it. if (count($this->migration->getSourcePlugin()->getIds()) == 1) { $sourceids = array(); foreach ($source_id_values as $source_id) { $sourceids[] = $source_id; } $this->getDatabase()->delete($this->mapTableName())->condition('sourceid1', $sourceids, 'IN')->execute(); $this->getDatabase()->delete($this->messageTableName())->condition('sourceid1', $sourceids, 'IN')->execute(); } else { foreach ($source_id_values as $source_id) { $map_query = $this->getDatabase()->delete($this->mapTableName()); $message_query = $this->getDatabase()->delete($this->messageTableName()); $count = 1; foreach ($source_id as $key_value) { $map_query->condition('sourceid' . $count, $key_value); $message_query->condition('sourceid' . $count++, $key_value); } $map_query->execute(); $message_query->execute(); } } }
/** * Manipulate file fields with any per field type processing. * * @param string $field_name * The field we're processing. * @param array $field_data * The an array of field type data from the source. * @param \Drupal\migrate\Entity\MigrationInterface $migration * The migration entity. */ protected function processFileField($field_name, $field_data, MigrationInterface $migration) { $process = ['plugin' => 'd6_cck_file', 'source' => [$field_name, $field_name . '_list', $field_name . '_data']]; $migration->mergeProcessOfProperty($field_name, $process); }
/** * Helper method to create an entity revision destination with mock services. * * @see \Drupal\Tests\migrate\Unit\Destination\EntityRevision * * @param $configuration * Configuration for the destination. * @param string $plugin_id * The plugin id. * @param array $plugin_definition * The plugin definition. * * @return \Drupal\Tests\migrate\Unit\destination\EntityRevision * Mocked destination. */ protected function getEntityRevisionDestination(array $configuration = [], $plugin_id = 'entity_revision', array $plugin_definition = []) { return new EntityRevision($configuration, $plugin_id, $plugin_definition, $this->migration->reveal(), $this->storage->reveal(), [], $this->entityManager->reveal(), $this->fieldTypeManager->reveal()); }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { $process = ['plugin' => 'iterator', 'source' => $field_name, 'process' => ['target_id' => 'fid', 'alt' => 'alt', 'title' => 'title', 'width' => 'width', 'height' => 'height']]; $migration->mergeProcessOfProperty($field_name, $process); }
/** * Class constructor. * * @param \Drupal\migrate\Entity\MigrationInterface $migration * The migration entity. * @param \Drupal\migrate\MigrateExecutable $migrate_executable * The migration executable. */ public function __construct(MigrationInterface $migration, MigrateExecutable $migrate_executable) { $this->migration = $migration; $this->migrateExecutable = $migrate_executable; $configuration = $migration->get('source'); if (!empty($configuration['cache_counts'])) { $this->cacheCounts = TRUE; } if (!empty($configuration['skip_count'])) { $this->skipCount = TRUE; } if (!empty($configuration['cache_key'])) { $this->cacheKey = $configuration['cache_key']; } if (!empty($configuration['track_changes'])) { $this->trackChanges = $configuration['track_changes']; } }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { $process = array('plugin' => 'iterator', 'source' => $field_name, 'process' => array('target_id' => 'tid')); $migration->setProcessOfProperty($field_name, $process); }
/** * Passes messages through to the map class. * * @param string $message * The message to record. * @param int $level * (optional) Message severity (defaults to MESSAGE_ERROR). */ public function saveMessage($message, $level = MigrationInterface::MESSAGE_ERROR) { $this->migration->getIdMap()->saveMessage($this->sourceIdValues, $message, $level); }
/** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { list($entity_type_id) = explode('__', $migration->id()); return new static($configuration, $plugin_id, $plugin_definition, $migration, $container->get('entity.manager')->getStorage($entity_type_id), array_keys($container->get('entity.manager')->getBundleInfo($entity_type_id)), $container->get('entity.manager'), $container->get('plugin.manager.field.field_type'), $container->get('password')); }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { $process = ['plugin' => 'iterator', 'source' => $field_name, 'process' => ['target_id' => 'fid', 'display' => 'display', 'description' => 'description']]; $migration->mergeProcessOfProperty($field_name, $process); }
/** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { $migration_configuration['migration'][] = $migration->id(); return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_configuration, $migration), $container->get('plugin.manager.menu.link'), $container->get('entity.manager')->getStorage('menu_link_content')); }
/** * Manipulate link fields with any per field type processing. * * @param string $field_name * The field we're processing. * @param array $field_data * The an array of field type data from the source. * @param \Drupal\migrate\Entity\MigrationInterface $migration * The migration entity. */ protected function processLinkField($field_name, $field_data, MigrationInterface $migration) { // Specifically process the link field until core is fixed. // @see https://www.drupal.org/node/2235457 $process = ['plugin' => 'd6_cck_link', 'source' => [$field_name, $field_name . '_title', $field_name . '_attributes']]; $migration->mergeProcessOfProperty($field_name, $process); }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { $process = ['plugin' => 'd6_cck_file', 'source' => [$field_name, $field_name . '_list', $field_name . '_data']]; $migration->mergeProcessOfProperty($field_name, $process); }
/** * {@inheritdoc} */ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) { $process = array(array('plugin' => 'iterator', 'source' => $field_name, 'process' => ['value' => 'value', 'format' => [['plugin' => 'static_map', 'bypass' => TRUE, 'source' => 'format', 'map' => [0 => NULL]], ['plugin' => 'skip_on_empty', 'method' => 'process'], ['plugin' => 'migration', 'migration' => 'd6_filter_format', 'source' => 'format']]])); $migration->setProcessOfProperty($field_name, $process); }