Мигрируем на Drupal 8! 
#dckiev14
About us 
Andy Postnikov http://dgo.to/@andypost 
Pavel Makhrinsky http://dgo.to/@gumanist
More then 2% of all sites 
How many sites are running Drupal 6 and 7?
Latest stats
10 Drupal myths 
●Box product 
●Lego 
●Contrib - themes, modules, libraries 
●Multilingual 
●Platform 
●Support 
●Community 
●Evolution
Go Drupal 8 
●PHP 5.4 - 5.5, 5.6.2 
●HTML 5 
●Libraries - jQuery 2.1 (3.0) 
●Modules 
●CDN - REST 
●Database
Common migration tasks 
●Planning 
oData mapping 
oDependencies management 
oData cleanup 
●Chicken 'n Egg handling 
●Continuous content lifecycle 
●Progress monitoring 
●Rollback results
What is the migrate module? 
Source => Process => Destination
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 freeze 
https://www.drupal.org/node/1052692#comment-6620570 
Initial patch was commited November 21, 2013 
https://www.drupal.org/node/2125717#comment-8197259 
Drupal 6 to Drupal 8 patch April 23, 2014 
https://www.drupal.org/node/2121299#comment-8710315 
Still in progress of polishing 
https://groups.drupal.org/imp - weekly call
Processing
Mapping
Definition (yml-file) 
id: migrate_example_people 
source: 
plugin: migrate_example_people 
destination: 
plugin: entity:user 
md5_passwords: true 
process: 
name: 
- 
plugin: concat 
delimiter: . 
source: 
- first_name 
- last_name 
- 
plugin: callback 
callable: 
- 'DrupalComponentUtilityUnicode' 
- 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 rows 
2.prepareRow(): add more data to a row 
3.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_type 
oentity:node 
oentity: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']); 
}
Demo 
●Drush 
●UI sandbox
Drush or custom code 
Recommended way to execute - DRUSH 
Custom code: 
public function submitForm(array &$form, array &$form_state) { 
/** @var $migration DrupalmigrateEntityMigrationInterface */ 
$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/imp 
IRC: Freenode #drupal-migrate
Links 
https://www.drupal.org/upgrade/migrate 
https://www.drupal.org/node/2127611 
https://groups.drupal.org/imp 
IRC: Freenode #drupal-migrate
Questions & Discussions 
Andy Postnikov http://dgo.to/@andypost 
Pavel Makhrinsky http://dgo.to/@gumanist 
Kiev 2014

Drupal 8 migrate!

  • 1.
  • 2.
    About us AndyPostnikov http://dgo.to/@andypost Pavel Makhrinsky http://dgo.to/@gumanist
  • 3.
    More then 2%of all sites How many sites are running Drupal 6 and 7?
  • 4.
  • 5.
    10 Drupal myths ●Box product ●Lego ●Contrib - themes, modules, libraries ●Multilingual ●Platform ●Support ●Community ●Evolution
  • 6.
    Go Drupal 8 ●PHP 5.4 - 5.5, 5.6.2 ●HTML 5 ●Libraries - jQuery 2.1 (3.0) ●Modules ●CDN - REST ●Database
  • 7.
    Common migration tasks ●Planning oData mapping oDependencies management oData cleanup ●Chicken 'n Egg handling ●Continuous content lifecycle ●Progress monitoring ●Rollback results
  • 8.
    What is themigrate module? Source => Process => Destination
  • 9.
    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
  • 10.
    Migrate in Drupal8 retrospective Signed after 6 weeks away from feature freeze https://www.drupal.org/node/1052692#comment-6620570 Initial patch was commited November 21, 2013 https://www.drupal.org/node/2125717#comment-8197259 Drupal 6 to Drupal 8 patch April 23, 2014 https://www.drupal.org/node/2121299#comment-8710315 Still in progress of polishing https://groups.drupal.org/imp - weekly call
  • 11.
  • 12.
  • 13.
    Definition (yml-file) id:migrate_example_people source: plugin: migrate_example_people destination: plugin: entity:user md5_passwords: true process: name: - plugin: concat delimiter: . source: - first_name - last_name - plugin: callback callable: - 'DrupalComponentUtilityUnicode' - strtolower - plugin: callback callable: trim - plugin: dedupe_entity entity_type: user field: name mail: email pass: pass roles: - plugin: explode delimiter: ';' source: groups
  • 14.
    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
  • 15.
    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
  • 16.
    Execution flow classMigrateExecutable { /** 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(); } }
  • 17.
    Source plugins Providessource rows - mostly custom 1.getIterator(): iterator producing rows 2.prepareRow(): add more data to a row 3.There are hooks for prepareRow() MigrateSourceInterface
  • 18.
    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; }
  • 19.
    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; }
  • 20.
    Process plugins ●Keysare destination properties ●Values are process pipelines ●Each pipeline is a series of process plugins + configuration ●There are shorthands MigrateProcessInterface::transform()
  • 21.
    Process pipelines process: id: - plugin: machine_name source: name - plugin: dedupe_entity entity_type: user_role field: id - plugin: user_update_8002 #custom
  • 22.
    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
  • 23.
    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); }
  • 24.
    Destination plugins ●Doesthe actual import ●Almost always provided by migrate module ●Most common is entity:$entity_type oentity:node oentity:user ●If you are writing one, you are doing it wrong MigrateDestinationInterface
  • 25.
    Destination plugins shipped ●Config ●Entity ●Null ●for custom tables: url_alias, user_data… destination: plugin: config config_name: user.mail
  • 26.
    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']); }
  • 27.
  • 28.
    Drush or customcode Recommended way to execute - DRUSH Custom code: public function submitForm(array &$form, array &$form_state) { /** @var $migration DrupalmigrateEntityMigrationInterface */ $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; }
  • 29.
    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/imp IRC: Freenode #drupal-migrate
  • 30.
    Links https://www.drupal.org/upgrade/migrate https://www.drupal.org/node/2127611 https://groups.drupal.org/imp IRC: Freenode #drupal-migrate
  • 31.
    Questions & Discussions Andy Postnikov http://dgo.to/@andypost Pavel Makhrinsky http://dgo.to/@gumanist Kiev 2014