drupal 8 migrate!
TRANSCRIPT
10 Drupal myths
●Box product●Lego●Contrib - themes, modules, libraries●Multilingual●Platform●Support●Community●Evolution
Common migration tasks
●Planningo Data mappingo Dependencies managemento Data cleanup
●Chicken 'n Egg handling●Continuous content lifecycle●Progress monitoring●Rollback results
Migrate vs Feeds
Mostly indentical feature set:●Since 2009 but FeedAPI 2007●feeds is UI-oriented●feeds_tamper vs custom code●feeds has a lot of satelite modules
Migrate in Drupal 8 retrospective
Signed after 6 weeks away from feature freezehttps://www.drupal.org/node/1052692#comment-6620570
Initial patch was commited November 21, 2013https://www.drupal.org/node/2125717#comment-8197259
Drupal 6 to Drupal 8 patch April 23, 2014https://www.drupal.org/node/2121299#comment-8710315
Still in progress of polishinghttps://groups.drupal.org/imp - weekly call
Definition (yml-file)id: migrate_example_peoplesource: plugin: migrate_example_peopledestination: plugin: entity:user md5_passwords: trueprocess: name: - plugin: concat delimiter: . source: - first_name - last_name - plugin: callback callable: - '\Drupal\Component\Utility\Unicode' - strtolower
- plugin: callback callable: trim - plugin: dedupe_entity entity_type: user field: name mail: email pass: pass roles: - plugin: explode delimiter: ';'
source: groups
Chicken and egg
process: tid: tid vid: plugin: migration migration: d6_taxonomy_vocabulary source: vid parent: - plugin: skip_process_on_empty source: parent - plugin: migration migration: d6_taxonomy_term
Dependencies
migration_dependencies: required: - d6_filter_format - d6_user_role - d6_user_picture_entity_display - d6_user_picture_entity_form_display optional: - d6_user_picture_file
Execution flow
class MigrateExecutable { /** Performs an import operation - migrate items from source to destination. */ public function import() { $source = $this->getSource(); $id_map = $this->migration->getIdMap(); $source->rewind(); while ($source->valid()) { $row = $source->current(); $this->processRow($row); $destination_id_values = $destination->import($row, $id_map->lookupDestinationId($this->sourceIdValues)); $id_map->saveIdMapping($row, $destination_id_values, $this->sourceRowStatus, $this->rollbackAction); $source->next(); } }
Source plugins
Provides source rows - mostly custom
1.getIterator(): iterator producing rows2.prepareRow(): add more data to a row3.There are hooks for prepareRow()
MigrateSourceInterface
Source example - core/** * Drupal 6 menu source from database. * * @MigrateSource( * id = "d6_menu", * source_provider = "menu" * ) */class Menu extends DrupalSqlBase {
/** * {@inheritdoc} */ public function query() { $query = $this->select('menu_custom', 'm') ->fields('m', array('menu_name', 'title', 'description')); return $query; }
Source example - custom
public function count() { if (is_array($this->getData())) { return count($this->getData()); } return 0; } public function getIterator() { return new \ArrayIterator($this->getData()); }
protected function getData() { if (!isset($this->data)) { $this->data = array(); foreach ($this->fetchDataFromYandexDirect() as $key => $value) { $this->data[$key] = (array) $value; } } return $this->data; }
Process plugins
●Keys are destination properties●Values are process pipelines●Each pipeline is a series of process plugins +
configuration●There are shorthands
MigrateProcessInterface::transform()
Process pipelines
process: id: - plugin: machine_name source: name - plugin: dedupe_entity entity_type: user_role field: id - plugin: user_update_8002 #custom
Process plugins shipped
Constant values
Plugins: get concat dedupebase iterator skip_row_if_not_set default_value extract
flatten iterator migration skip_process_on_empty skip_row_on_empty static map machine_name dedupe_entity callback
Process plugin example
public function transform($value, MigrateExecutable $migrate_executable, Row $row, $destination_property) { $new_value = $this->getTransliteration()->transliterate($value, LanguageInterface::LANGCODE_DEFAULT, '_'); $new_value = strtolower($new_value); $new_value = preg_replace('/[^a-z0-9_]+/', '_', $new_value); return preg_replace('/_+/', '_', $new_value); }
Destination plugins
●Does the actual import●Almost always provided by migrate module●Most common is entity:$entity_typeo entity:nodeo entity:user
●If you are writing one, you are doing it wrong
MigrateDestinationInterface
Destination plugins shipped
●Config●Entity●Null●for custom tables: url_alias, user_data…
destination: plugin: config config_name: user.mail
Destination plugin example
/** * {@inheritdoc} */ public function import(Row $row, array $old_destination_id_values = array()) {
$path = $this->aliasStorage->save( $row->getDestinationProperty('source'), $row->getDestinationProperty('alias'), $row->getDestinationProperty('langcode'), $old_destination_id_values ? $old_destination_id_values[0] : NULL );
return array($path['pid']); }
Drush or custom code
Recommended way to execute - DRUSH
Custom code: public function submitForm(array &$form, array &$form_state) { /** @var $migration \Drupal\migrate\Entity\MigrationInterface */ $migration = entity_load('migration', $form_state['values']['migration']);
$executable = new MigrateExecutable($migration, $this); $executable->import(); // Display statistics. $this->getStats($form, $form_state); $form_state['rebuild'] = TRUE; }
How to help
Document driven development http://dgo.to/2127611
Open issues of migration system:http://goo.gl/fmVNQl
Drupal groups http://dgo.to/g/impIRC: Freenode #drupal-migrate
Links
https://www.drupal.org/upgrade/migratehttps://www.drupal.org/node/2127611https://groups.drupal.org/impIRC: Freenode #drupal-migrate