1237 lines
51 KiB
PHP
1237 lines
51 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Advanced migration examples. These serve two purposes:
|
|
*
|
|
* 1. To demonstrate some of the more advanced usages of the Migrate module.
|
|
* Search for "TIP:" below for features not found in the basic example.
|
|
* 2. To provide thorough test cases for the simpletest suite.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Abstract intermediate class holding common settings.
|
|
*/
|
|
abstract class AdvancedExampleMigration extends Migration {
|
|
public $basicFormat;
|
|
|
|
public function __construct() {
|
|
// TIP: Migrations can be organized into groups. In this case, all the migrations
|
|
// derived from AdvancedExampleMigration will be part of the 'wine' group.
|
|
// This enables us to easily run just the wine example migrations:
|
|
// drush migrate-import --group=wine
|
|
// The second argument to MigrateGroup::getInstance is an array of groups
|
|
// which should come before this when viewing migration statuses, or running
|
|
// migration operations using --all. Since the beer migrations in this module
|
|
// did not specify a group, it is in the 'default' group, so this constructor
|
|
// indicates that the wine migrations come after the beer migrations.
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
|
|
$this->team = array(
|
|
new MigrateTeamMember('Jack Kramer', 'jkramer@example.com', t('Taster')),
|
|
new MigrateTeamMember('Linda Madison', 'lmadison@example.com', t('Winemaker')),
|
|
);
|
|
$this->issuePattern = 'http://drupal.org/node/:id:';
|
|
|
|
// A format of our own, for testing migration of formats
|
|
$this->basicFormat = filter_format_load('migrate_example');
|
|
|
|
// We can do shared field mappings in the common class
|
|
if (module_exists('path')) {
|
|
$this->addFieldMapping('path')
|
|
->issueGroup(t('DNM'));
|
|
if (module_exists('pathauto')) {
|
|
$this->addFieldMapping('pathauto')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: While usually you'll create true migrations - processes that copy data
|
|
* from some source into Drupal - you can also define processing steps for either
|
|
* the import or rollback stages that take other actions. In this case, we want
|
|
* to disable auto_nodetitle while the migration steps run. We'll re-enable it
|
|
* over in WineFinishMigration.
|
|
*/
|
|
class WinePrepMigration extends MigrationBase {
|
|
// Track whether the auto_nodetitle was originally enabled so we know whether
|
|
// to re-enable it. This is public so WineFinishMigration can reference it.
|
|
public static $wasEnabled = FALSE;
|
|
|
|
public function __construct() {
|
|
// Because we're derived directly from migrationBase rather than AdvancedExampleMigration,
|
|
// we must specify the group again here.
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('If auto_nodetitle is present, disable it for the duration');
|
|
}
|
|
// Define isComplete(), returning a boolean, to indicate whether dependent
|
|
// migrations may proceed
|
|
public function isComplete() {
|
|
// If Auto Node Title is disabled, other migrations are free to go
|
|
if (module_exists('auto_nodetitle')) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
// Implement any action you want to occur during an import process in an
|
|
// import() method (alternatively, if you have an action which you want to
|
|
// run during rollbacks, define a rollback() method).
|
|
public function import() {
|
|
if (module_exists('auto_nodetitle')) {
|
|
self::$wasEnabled = TRUE;
|
|
module_disable(array('auto_nodetitle'));
|
|
self::displayMessage(t('Disabled auto_nodetitle module'), 'success');
|
|
}
|
|
else {
|
|
self::$wasEnabled = FALSE;
|
|
self::displayMessage(t('Auto_nodetitle is already disabled'), 'success');
|
|
}
|
|
// Must return one of the MigrationBase RESULT constants
|
|
return MigrationBase::RESULT_COMPLETED;
|
|
}
|
|
}
|
|
|
|
// The term migrations are very similar - implement the commonalities here
|
|
abstract class WineTermMigration extends AdvancedExampleMigration {
|
|
public function __construct($type, $vocabulary_name, $description) {
|
|
parent::__construct();
|
|
$this->description = $description;
|
|
$this->dependencies = array('WinePrep');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'categoryid' => array('type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationTerm::getKeySchema()
|
|
);
|
|
|
|
$query = db_select('migrate_example_wine_categories', 'wc')
|
|
->fields('wc', array('categoryid', 'name', 'details', 'category_parent', 'ordering'))
|
|
->condition('type', $type)
|
|
// This sort assures that parents are saved before children.
|
|
->orderBy('category_parent', 'ASC');
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationTerm($vocabulary_name);
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('name', 'name');
|
|
$this->addFieldMapping('description', 'details');
|
|
$this->addFieldMapping('parent', 'category_parent')
|
|
->sourceMigration($this->getMachineName());
|
|
$this->addFieldMapping('weight', 'ordering');
|
|
$this->addFieldMapping('format')
|
|
->defaultValue($this->basicFormat->format);
|
|
|
|
// Unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addFieldMapping('parent_name')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
|
|
class WineVarietyMigration extends WineTermMigration {
|
|
public function __construct() {
|
|
parent::__construct('variety', 'migrate_example_wine_varieties',
|
|
t('Migrate varieties from the source database to taxonomy terms'));
|
|
}
|
|
}
|
|
|
|
class WineRegionMigration extends WineTermMigration {
|
|
public function __construct() {
|
|
parent::__construct('region', 'migrate_example_wine_regions',
|
|
t('Migrate regions from the source database to taxonomy terms'));
|
|
}
|
|
}
|
|
|
|
class WineBestWithMigration extends WineTermMigration {
|
|
public function __construct() {
|
|
parent::__construct('best_with', 'migrate_example_wine_best_with',
|
|
t('Migrate "Best With" from the source database to taxonomy terms'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: Files can be migrated directly by themselves, by using the MigrateDestinationFile
|
|
* class. This will copy the files themselves from the source, and set up the
|
|
* Drupal file tables appropriately.
|
|
*/
|
|
class WineFileCopyMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Profile images');
|
|
$this->dependencies = array('WinePrep');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('imageid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Image ID.'
|
|
)
|
|
),
|
|
MigrateDestinationFile::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_files', 'wf')
|
|
->fields('wf', array('imageid', 'url'))
|
|
->isNull('wineid');
|
|
$this->source = new MigrateSourceSQL($query);
|
|
|
|
$this->destination = new MigrateDestinationFile();
|
|
|
|
// In the simplest case, just map the incoming URL to 'value'.
|
|
$this->addFieldMapping('value', 'url');
|
|
|
|
$this->addUnmigratedDestinations(array('fid', 'uid', 'timestamp',
|
|
'destination_dir', 'destination_file', 'source_dir', 'preserve_files',
|
|
'file_replace'));
|
|
$this->removeFieldMapping('pathauto');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Migration class to test importing from a BLOB column into a file entity.
|
|
* Typically, one would use this OR import into a File Field.
|
|
* @see MigrateExampleOracleNode()
|
|
*/
|
|
class WineFileBlobMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Example migration from BLOB column into files.');
|
|
$this->dependencies = array('WinePrep');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('imageid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Image ID.'
|
|
)
|
|
),
|
|
MigrateDestinationFile::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_blobs', 'wf')
|
|
->fields('wf', array('imageid', 'imageblob'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
|
|
// Note that the WineFileCopyMigration example let the second argument,
|
|
// the file_class, to default to MigrateFileUri, indicating that the
|
|
// 'value' we're passing was a URI. In this case, we're passing a blob, so
|
|
// tell the destination to expect it.
|
|
$this->destination = new MigrateDestinationFile('file', 'MigrateFileBlob');
|
|
|
|
// Basic fields
|
|
$this->addFieldMapping('uid')
|
|
->defaultValue(1);
|
|
// The destination filename must be specified for blobs
|
|
$this->addFieldMapping('destination_file')
|
|
->defaultValue('druplicon.png');
|
|
$this->addFieldMapping('value', 'imageblob')
|
|
->description('An image blob in the DB');
|
|
|
|
// Unmapped destination fields
|
|
$this->addUnmigratedDestinations(array('fid', 'timestamp',
|
|
'destination_dir', 'file_replace', 'preserve_files'));
|
|
|
|
// Our base class mapped this since most migrations use it, but not this one,
|
|
// so remove it
|
|
$this->removeFieldMapping('pathauto');
|
|
}
|
|
}
|
|
|
|
class WineRoleMigration extends XMLMigration {
|
|
public function __construct() {
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('XML feed (multi items) of roles (positions)');
|
|
|
|
// TIP: Regular dependencies, besides enforcing (in the absence of --force)
|
|
// the run order of migrations, affect the sorting of migrations on display.
|
|
// You can use soft dependencies to affect just the display order when the
|
|
// migrations aren't technically required to run in a certain order. In this
|
|
// case, we want the role migration to appear after the file migration.
|
|
$this->softDependencies = array('WineFileCopy');
|
|
|
|
// There isn't a consistent way to automatically identify appropriate "fields"
|
|
// from an XML feed, so we pass an explicit list of source fields
|
|
$fields = array(
|
|
'name' => t('Position name'),
|
|
);
|
|
|
|
// The source ID here is the one retrieved from each data item in the XML file, and
|
|
// used to identify specific items
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'sourceid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationRole::getKeySchema()
|
|
);
|
|
|
|
// IMPORTANT: Do not try this at home! We have included importable files
|
|
// with the migrate_example module so it can be very simply installed and
|
|
// run, but you should never include any data you want to keep private
|
|
// (especially user data like email addresses, phone numbers, etc.) in the
|
|
// module directory. Your source data should be outside of the webroot, and
|
|
// should not be anywhere where it may get committed into a revision control
|
|
// system.
|
|
|
|
// This can also be an URL instead of a file path.
|
|
$xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
|
|
$items_url = $xml_folder . 'positions.xml';
|
|
$item_xpath = '/positions/position'; // relative to document
|
|
$item_ID_xpath = 'sourceid'; // relative to item_xpath and gets assembled
|
|
// into full path /producers/producer/sourceid
|
|
|
|
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
|
|
$this->source = new MigrateSourceMultiItems($items_class, $fields);
|
|
|
|
$this->destination = new MigrateDestinationRole();
|
|
|
|
$this->addFieldMapping('name', 'name')
|
|
->xpath('name');
|
|
$this->addUnmigratedDestinations(array('weight'));
|
|
}
|
|
}
|
|
|
|
class WineUserMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Wine Drinkers of the world');
|
|
$this->dependencies = array('WinePrep', 'WineFileCopy', 'WineRole');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('accountid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Account ID.'
|
|
)
|
|
),
|
|
MigrateDestinationUser::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_account', 'wa')
|
|
->fields('wa', array('accountid', 'status', 'posted', 'name',
|
|
'password', 'mail', 'last_access', 'last_login',
|
|
'original_mail', 'sig', 'sex', 'imageid', 'positions'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationUser();
|
|
|
|
// Mapped fields
|
|
$this->addSimpleMappings(array('name', 'status', 'mail'));
|
|
$this->addFieldMapping('created', 'posted')
|
|
->description('See prepare method');
|
|
$this->addFieldMapping('access', 'last_access')
|
|
->description('See prepare method');
|
|
$this->addFieldMapping('login', 'last_login')
|
|
->description('See prepare method');
|
|
$this->addFieldMapping('pass', 'password');
|
|
$this->addFieldMapping('roles', 'positions')
|
|
->separator(',')
|
|
->sourceMigration('WineRole');
|
|
$this->addFieldMapping('signature', 'sig');
|
|
$this->addFieldMapping('signature_format')
|
|
->defaultValue($this->basicFormat->format);
|
|
$this->addFieldMapping('init', 'original_mail');
|
|
$this->addFieldMapping('field_migrate_example_gender', 'sex')
|
|
->description(t('Map from M/F to 0/1 in prepare method'));
|
|
$this->addFieldMapping('picture', 'imageid')
|
|
->sourceMigration('WineFileCopy');
|
|
|
|
// Unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addUnmigratedDestinations(array('theme', 'timezone', 'language',
|
|
'is_new', 'field_migrate_example_favbeers', 'role_names'));
|
|
}
|
|
|
|
public function prepare(stdClass $account, stdClass $row) {
|
|
// Source dates are in ISO format.
|
|
// Because the mappings above have been applied, $account->created contains
|
|
// the date/time string now - we could also pass $row->posted here.
|
|
$account->created = strtotime($account->created);
|
|
$account->access = strtotime($account->access);
|
|
$account->login = strtotime($account->login);
|
|
|
|
// Gender data comes in as M/F, needs to be saved as Male=0/Female=1
|
|
// TIP: Note that the Migration prepare method is called after all other
|
|
// prepare handlers. Most notably, the field handlers have had their way
|
|
// and created field arrays, so we have to save in the same format.
|
|
switch ($row->sex) {
|
|
case 'm':
|
|
case 'M':
|
|
$account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 0;
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
$account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 1;
|
|
break;
|
|
default:
|
|
unset($account->field_migrate_example_gender);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
class WineProducerMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Wine producers of the world');
|
|
$this->dependencies = array('WineRegion', 'WineUser');
|
|
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'producerid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'alias' => 'p',
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
$query = db_select('migrate_example_wine_producer', 'p')
|
|
->fields('p', array('producerid', 'name', 'body', 'excerpt', 'accountid'));
|
|
// Region term is singletons, handled straighforwardly
|
|
$query->leftJoin('migrate_example_wine_category_producer', 'reg',
|
|
"p.producerid = reg.producerid");
|
|
$query->addField('reg', 'categoryid', 'region');
|
|
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationNode('migrate_example_producer');
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('title', 'name')
|
|
->description(t('Mapping producer name in source to node title'));
|
|
$this->addFieldMapping('uid', 'accountid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(1);
|
|
$this->addFieldMapping('migrate_example_wine_regions', 'region')
|
|
->sourceMigration('WineRegion');
|
|
$this->addFieldMapping('migrate_example_wine_regions:source_type')
|
|
->defaultValue('tid');
|
|
$this->addFieldMapping('body', 'body');
|
|
$this->addFieldMapping('body:summary', 'excerpt');
|
|
$this->addFieldMapping('sticky')
|
|
->defaultValue(0);
|
|
|
|
// No unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addUnmigratedDestinations(array('is_new', 'created', 'changed',
|
|
'status', 'promote', 'revision', 'language', 'revision_uid', 'log', 'tnid',
|
|
'body:format', 'body:language', 'migrate_example_wine_regions:create_term',
|
|
'comment'));
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: An example of importing from an XML feed. See the files in the xml
|
|
* directory - index.xml contains a list of IDs to import, and <id>.xml
|
|
* is the data for a given producer.
|
|
*
|
|
* Note that, if basing a migration on an XML source, you need to derive it
|
|
* from XMLMigration instead of Migration.
|
|
*/
|
|
class WineProducerXMLMigration extends XMLMigration {
|
|
public function __construct() {
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('XML feed of wine producers of the world');
|
|
$this->dependencies = array('WineRegion', 'WineUser');
|
|
|
|
// There isn't a consistent way to automatically identify appropriate "fields"
|
|
// from an XML feed, so we pass an explicit list of source fields
|
|
$fields = array(
|
|
'name' => t('Producer name'),
|
|
'description' => t('Description of producer'),
|
|
'authorid' => t('Numeric ID of the author'),
|
|
'region' => t('Name of region'),
|
|
);
|
|
|
|
// The source ID here is the one retrieved from the XML listing file, and
|
|
// used to identify the specific item's file
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'sourceid' => array(
|
|
'type' => 'varchar',
|
|
'length' => 4,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
// IMPORTANT: Do not try this at home! We have included importable files
|
|
// with the migrate_example module so it can be very simply installed and
|
|
// run, but you should never include any data you want to keep private
|
|
// (especially user data like email addresses, phone numbers, etc.) in the
|
|
// module directory. Your source data should be outside of the webroot, and
|
|
// should not be anywhere where it may get committed into a revision control
|
|
// system.
|
|
|
|
// This can also be an URL instead of a file path.
|
|
$xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
|
|
$list_url = $xml_folder . 'index.xml';
|
|
// Each ID retrieved from the list URL will be plugged into :id in the
|
|
// item URL to fetch the specific objects.
|
|
$item_url = $xml_folder . ':id.xml';
|
|
|
|
// We use the MigrateSourceList class for any source where we obtain the list
|
|
// of IDs to process separately from the data for each item. The listing
|
|
// and item are represented by separate classes, so for example we could
|
|
// replace the XML listing with a file directory listing, or the XML item
|
|
// with a JSON item.
|
|
$this->source = new MigrateSourceList(new MigrateListXML($list_url),
|
|
new MigrateItemXML($item_url), $fields);
|
|
|
|
$this->destination = new MigrateDestinationNode('migrate_example_producer');
|
|
|
|
// TIP: Note that for XML sources, in addition to the source field passed to
|
|
// addFieldMapping (the name under which it will be saved in the data row
|
|
// passed through the migration process) we specify the Xpath used to retrieve
|
|
// the value from the XML.
|
|
$this->addFieldMapping('title', 'name')
|
|
->xpath('/producer/name');
|
|
$this->addFieldMapping('uid', 'authorid')
|
|
->xpath('/producer/authorid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(1);
|
|
$this->addFieldMapping('migrate_example_wine_regions', 'region')
|
|
->xpath('/producer/region');
|
|
$this->addFieldMapping('body', 'description')
|
|
->xpath('/producer/description');
|
|
|
|
$this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
|
|
'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
|
|
'is_new', 'body:summary', 'body:format', 'body:language',
|
|
'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
|
|
'comment'));
|
|
if (module_exists('path')) {
|
|
$this->addFieldMapping('path')
|
|
->issueGroup(t('DNM'));
|
|
if (module_exists('pathauto')) {
|
|
$this->addFieldMapping('pathauto')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: An example of importing from an XML feed where both the id and the
|
|
* data to import are in the same file. The id is a part of the data. See
|
|
* the file in the xml directory - producers.xml which contains all IDs and
|
|
* producer data for this example.
|
|
*
|
|
* Note that, if basing a migration on an XML source, you need to derive it
|
|
* from XMLMigration instead of Migration.
|
|
*/
|
|
class WineProducerMultiXMLMigration extends XMLMigration {
|
|
public function __construct() {
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('XML feed (multi items) of wine producers of the world');
|
|
$this->dependencies = array('WineRegion', 'WineUser');
|
|
|
|
// There isn't a consistent way to automatically identify appropriate "fields"
|
|
// from an XML feed, so we pass an explicit list of source fields
|
|
$fields = array(
|
|
'name' => t('Producer name'),
|
|
'description' => t('Description of producer'),
|
|
'authorid' => t('Numeric ID of the author'),
|
|
'region' => t('Name of region'),
|
|
);
|
|
|
|
// The source ID here is the one retrieved from each data item in the XML file, and
|
|
// used to identify specific items
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'sourceid' => array(
|
|
'type' => 'varchar',
|
|
'length' => 4,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
// IMPORTANT: Do not try this at home! We have included importable files
|
|
// with the migrate_example module so it can be very simply installed and
|
|
// run, but you should never include any data you want to keep private
|
|
// (especially user data like email addresses, phone numbers, etc.) in the
|
|
// module directory. Your source data should be outside of the webroot, and
|
|
// should not be anywhere where it may get committed into a revision control
|
|
// system.
|
|
|
|
// This can also be an URL instead of a file path.
|
|
$xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
|
|
$items_url = $xml_folder . 'producers.xml';
|
|
|
|
// We use the MigrateSourceMultiItems class for any source where we obtain the list
|
|
// of IDs to process and the data for each item from the same file. Typically the data
|
|
// for an item is not contained in a single line within the source file. Examples include
|
|
// multiple items defined in a single xml file or a single json file where in both cases
|
|
// the id is part of the item.
|
|
|
|
$item_xpath = '/producers/producer'; // relative to document
|
|
|
|
$item_ID_xpath = 'sourceid'; // relative to item_xpath and gets assembled
|
|
// into full path /producers/producer/sourceid
|
|
|
|
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
|
|
$this->source = new MigrateSourceMultiItems($items_class, $fields);
|
|
|
|
$this->destination = new MigrateDestinationNode('migrate_example_producer');
|
|
|
|
// TIP: Note that for XML sources, in addition to the source field passed to
|
|
// addFieldMapping (the name under which it will be saved in the data row
|
|
// passed through the migration process) we specify the Xpath used to retrieve
|
|
// the value from the XML.
|
|
// TIP: Note that all xpaths for fields begin at the last element of the item
|
|
// xpath since each item xml chunk is processed individually.
|
|
// (ex. xpath=name is equivalent to a full xpath of /producers/producer/name)
|
|
$this->addFieldMapping('title', 'name')
|
|
->xpath('name');
|
|
$this->addFieldMapping('uid', 'authorid')
|
|
->xpath('authorid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(1);
|
|
$this->addFieldMapping('migrate_example_wine_regions', 'region')
|
|
->xpath('region');
|
|
$this->addFieldMapping('body', 'description')
|
|
->xpath('description');
|
|
|
|
$this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
|
|
'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
|
|
'is_new', 'body:summary', 'body:format', 'body:language',
|
|
'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
|
|
'comment'));
|
|
if (module_exists('path')) {
|
|
$this->addFieldMapping('path')
|
|
->issueGroup(t('DNM'));
|
|
if (module_exists('pathauto')) {
|
|
$this->addFieldMapping('pathauto')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: An alternative approach using MigrateSourceSQL. This uses a different
|
|
* XML library, which advances element-by-element through the XML file rather
|
|
* than reading in the whole file. This source will work better with large XML
|
|
* files, but is slower for small files and has a more restrictive query lanaguage
|
|
* for selecting the elements to process.
|
|
*/
|
|
class WineProducerXMLPullMigration extends XMLMigration {
|
|
public function __construct() {
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('XML feed (pull) of wine producers of the world');
|
|
$this->dependencies = array('WineRegion', 'WineUser');
|
|
|
|
$fields = array(
|
|
'name' => t('Producer name'),
|
|
'description' => t('Description of producer'),
|
|
'authorid' => t('Numeric ID of the author'),
|
|
'region' => t('Name of region'),
|
|
);
|
|
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'sourceid' => array(
|
|
'type' => 'varchar',
|
|
'length' => 4,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
// IMPORTANT: Do not try this at home! We have included importable files
|
|
// with the migrate_example module so it can be very simply installed and
|
|
// run, but you should never include any data you want to keep private
|
|
// (especially user data like email addresses, phone numbers, etc.) in the
|
|
// module directory. Your source data should be outside of the webroot, and
|
|
// should not be anywhere where it may get committed into a revision control
|
|
// system.
|
|
|
|
// This can also be an URL instead of a file path.
|
|
$xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
|
|
$items_url = $xml_folder . 'producers2.xml';
|
|
|
|
// As with MigrateSourceMultiItems, this applies where there is not a separate
|
|
// list of IDs to process - the source XML file is entirely self-contained.
|
|
// For the ID path, and xpath for each component, we can use the full xpath
|
|
// syntax as usual. However, the syntax to select the elements that correspond
|
|
// to objects to import is more limited. It must be a fully-qualified path
|
|
// to the element (i.e., /producers/producer rather than just //producer).
|
|
$item_xpath = '/producers/producer'; // relative to document
|
|
$item_ID_xpath = 'sourceid'; // relative to item_xpath
|
|
|
|
$this->source = new MigrateSourceXML($items_url, $item_xpath, $item_ID_xpath,
|
|
$fields);
|
|
|
|
$this->destination = new MigrateDestinationNode('migrate_example_producer');
|
|
$this->addFieldMapping('title', 'name')
|
|
->xpath('name');
|
|
$this->addFieldMapping('uid', 'authorid')
|
|
->xpath('authorid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(1);
|
|
$this->addFieldMapping('migrate_example_wine_regions', 'region')
|
|
->xpath('region');
|
|
$this->addFieldMapping('body', 'description')
|
|
->xpath('description');
|
|
|
|
$this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
|
|
'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
|
|
'is_new', 'body:summary', 'body:format', 'body:language',
|
|
'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
|
|
'comment'));
|
|
if (module_exists('path')) {
|
|
$this->addFieldMapping('path')
|
|
->issueGroup(t('DNM'));
|
|
if (module_exists('pathauto')) {
|
|
$this->addFieldMapping('pathauto')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Add node_reference field pointing to producer
|
|
class WineWineMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Wines of the world');
|
|
$this->dependencies = array('WineVariety', 'WineRegion',
|
|
'WineBestWith', 'WineUser', 'WineProducer');
|
|
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'wineid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Wine ID',
|
|
'alias' => 'w',
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
$query = db_select('migrate_example_wine', 'w')
|
|
->fields('w', array('wineid', 'name', 'body', 'excerpt', 'accountid',
|
|
'posted', 'last_changed', 'variety', 'region', 'rating'));
|
|
$query->leftJoin('migrate_example_wine_category_wine', 'cwbw',
|
|
"w.wineid = cwbw.wineid");
|
|
$query->leftJoin('migrate_example_wine_categories', 'bw',
|
|
"cwbw.categoryid = bw.categoryid AND bw.type = 'best_with'");
|
|
// Gives a single comma-separated list of related terms
|
|
$query->groupBy('w.wineid');
|
|
$query->addExpression('GROUP_CONCAT(bw.categoryid)', 'best_with');
|
|
|
|
$count_query = db_select('migrate_example_wine', 'w');
|
|
$count_query->addExpression('COUNT(wineid)', 'cnt');
|
|
|
|
// TIP: By passing an array of source fields to the MigrateSourceSQL constructor,
|
|
// we can modify the descriptions of source fields (which just default, for
|
|
// SQL migrations, to table_alias.column_name), as well as add additional fields
|
|
// (which may be populated in prepareRow()).
|
|
$source_fields = array(
|
|
'wineid' => t('Wine ID in the old system'),
|
|
'name' => t('The name of the wine'),
|
|
'best_vintages' => t('What years were best for this wine?'),
|
|
'url' => t('Image URLs attached to this wine; populated in prepareRow()'),
|
|
'image_alt' => t('Image alt text attached to this wine; populated in prepareRow()'),
|
|
'image_title' => t('Image titles attached to this wine; populated in prepareRow()'),
|
|
);
|
|
|
|
// TIP: By default, each time a migration is run, any previously unimported source items
|
|
// are imported (along with any previously-imported items marked for update). If the
|
|
// source data contains a timestamp that is set to the creation time of each new item,
|
|
// as well as set to the update time for any existing items that are updated, then
|
|
// you can have those updated items automatically reimported by setting the field as
|
|
// your highwater field.
|
|
$this->highwaterField = array(
|
|
'name' => 'last_changed', // Column to be used as highwater mark
|
|
'alias' => 'w', // Table alias containing that column
|
|
'type' => 'int', // By default, highwater marks are assumed to be lexicographically
|
|
// sortable (e.g., '2011-05-19 17:53:12'). To properly
|
|
// deal with integer highwater marks (such as UNIX
|
|
// timestamps), indicate so here.
|
|
);
|
|
// Note that it is important to process rows in the order of the highwater mark
|
|
$query->orderBy('last_changed');
|
|
|
|
$this->source = new MigrateSourceSQL($query, $source_fields, $count_query);
|
|
$this->destination = new MigrateDestinationNode('migrate_example_wine');
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('title', 'name')
|
|
->description(t('Mapping wine name in source to node title'));
|
|
$this->addFieldMapping('uid', 'accountid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(1);
|
|
// TIP: By default, term relationship are assumed to be passed by name.
|
|
// In this case, the source values are IDs, so we specify the relevant
|
|
// migration (so the tid can be looked up in the map), and tell the term
|
|
// field handler that it is receiving tids instead of names
|
|
$this->addFieldMapping('migrate_example_wine_varieties', 'variety')
|
|
->sourceMigration('WineVariety');
|
|
$this->addFieldMapping('migrate_example_wine_varieties:source_type')
|
|
->defaultValue('tid');
|
|
$this->addFieldMapping('migrate_example_wine_regions', 'region')
|
|
->sourceMigration('WineRegion');
|
|
$this->addFieldMapping('migrate_example_wine_regions:source_type')
|
|
->defaultValue('tid');
|
|
$this->addFieldMapping('migrate_example_wine_best_with', 'best_with')
|
|
->separator(',')
|
|
->sourceMigration('WineBestWith');
|
|
$this->addFieldMapping('migrate_example_wine_best_with:source_type')
|
|
->defaultValue('tid');
|
|
$this->addFieldMapping('field_migrate_example_wine_ratin', 'rating');
|
|
$this->addFieldMapping('field_migrate_example_top_vintag', 'best_vintages');
|
|
|
|
// TIP: You can apply one or more functions to a source value using ->callbacks().
|
|
// The function must take a single argument and return a value which is a
|
|
// transformation of the argument. As this example shows, you can have multiple
|
|
// callbacks, and they can either be straight functions or class methods. In
|
|
// this case, our custom method prepends 'review: ' to the body, and then we
|
|
// call a standard Drupal function to uppercase the whole body.
|
|
$this->addFieldMapping('body', 'body')
|
|
->callbacks(array($this, 'addTitlePrefix'), 'drupal_strtoupper');
|
|
$this->addFieldMapping('body:summary', 'excerpt');
|
|
|
|
// We will get the image data from a related table in prepareRow()
|
|
$this->addFieldMapping('field_migrate_example_image', 'url');
|
|
// Indicate that we want each file to maintain its name, replacing any
|
|
// previous file of the same name (as opposed to being renamed to avoid
|
|
// collisions, which is the default).
|
|
$this->addFieldMapping('field_migrate_example_image:file_replace')
|
|
->defaultValue(FILE_EXISTS_REPLACE);
|
|
$this->addFieldMapping('field_migrate_example_image:alt', 'image_alt');
|
|
$this->addFieldMapping('field_migrate_example_image:title', 'image_title');
|
|
|
|
$this->addFieldMapping('sticky')
|
|
->defaultValue(0);
|
|
// These are already UNIX timestamps, so just pass through
|
|
$this->addFieldMapping('created', 'posted');
|
|
$this->addFieldMapping('changed', 'last_changed');
|
|
|
|
// No unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addUnmigratedDestinations(array('revision_uid', 'status', 'promote',
|
|
'revision', 'log', 'language', 'tnid', 'is_new', 'body:format',
|
|
'body:language', 'migrate_example_wine_regions:create_term', 'comment',
|
|
'migrate_example_wine_varieties:create_term', 'migrate_example_wine_best_with:create_term',
|
|
'field_migrate_example_image:language', 'field_migrate_example_image:preserve_files',
|
|
'field_migrate_example_image:source_dir', 'field_migrate_example_image:destination_dir',
|
|
'field_migrate_example_image:destination_file', 'field_migrate_example_image:file_class',
|
|
));
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
|
|
protected function addTitlePrefix($source_title) {
|
|
return t('review: !title', array('!title' => $source_title));
|
|
}
|
|
|
|
// TIP: Implement a prepareRow() method to manipulate the source row between
|
|
// retrieval from the database and the automatic applicaton of mappings
|
|
public function prepareRow($current_row) {
|
|
// We used the MySQL GROUP_CONCAT function above to handle a multi-value source
|
|
// field - more portably, we query the related table with multiple values here,
|
|
// so the values can run through the mapping process
|
|
$source_id = $current_row->wineid;
|
|
$result = db_select('migrate_example_wine_vintages', 'v')
|
|
->fields('v', array('vintage'))
|
|
->condition('wineid', $source_id)
|
|
->execute();
|
|
foreach ($result as $row) {
|
|
$current_row->best_vintages[] = $row->vintage;
|
|
}
|
|
|
|
// We can have multiple files per node, so we pull them here along with
|
|
// their related data (alt/title).
|
|
$result = db_select('migrate_example_wine_files', 'f')
|
|
->fields('f', array('url', 'image_alt', 'image_title'))
|
|
->condition('wineid', $source_id)
|
|
->execute();
|
|
foreach ($result as $row) {
|
|
$current_row->url[] = $row->url;
|
|
$current_row->image_alt[] = $row->image_alt;
|
|
$current_row->image_title[] = $row->image_title;
|
|
}
|
|
|
|
// We could also have used this function to decide to skip a row, in cases
|
|
// where that couldn't easily be done through the original query. Simply
|
|
// return FALSE in such cases.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
class WineCommentMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = 'Comments about wines';
|
|
$this->dependencies = array('WineUser', 'WineWine');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('commentid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationComment::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_comment', 'wc')
|
|
->fields('wc', array('commentid', 'comment_parent', 'name', 'mail',
|
|
'accountid', 'body', 'wineid', 'subject', 'commenthost', 'userpage',
|
|
'posted', 'lastchanged'))
|
|
->orderBy('comment_parent');
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationComment('comment_node_migrate_example_wine');
|
|
|
|
// Mapped fields
|
|
$this->addSimpleMappings(array('name', 'subject', 'mail'));
|
|
$this->addFieldMapping('status')
|
|
->defaultValue(COMMENT_PUBLISHED);
|
|
$this->addFieldMapping('nid', 'wineid')
|
|
->sourceMigration('WineWine');
|
|
$this->addFieldMapping('uid', 'accountid')
|
|
->sourceMigration('WineUser')
|
|
->defaultValue(0);
|
|
$this->addFieldMapping('pid', 'comment_parent')
|
|
->sourceMigration('WineComment')
|
|
->description('Parent comment');
|
|
$this->addFieldMapping('comment_body', 'body');
|
|
$this->addFieldMapping('hostname', 'commenthost');
|
|
$this->addFieldMapping('created', 'posted');
|
|
$this->addFieldMapping('changed', 'lastchanged');
|
|
$this->addFieldMapping('homepage', 'userpage');
|
|
|
|
// No unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addUnmigratedDestinations(array('thread', 'language',
|
|
'comment_body:format', 'comment_body:language'));
|
|
$this->removeFieldMapping('pathauto');
|
|
}
|
|
}
|
|
|
|
// TIP: An easy way to simply migrate into a Drupal table (i.e., one defined
|
|
// through the Schema API) is to use the MigrateDestinationTable destination.
|
|
// Just pass the table name to getKeySchema and the MigrateDestinationTable constructor.
|
|
class WineTableMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = 'Miscellaneous table data';
|
|
$this->softDependencies = array('WineComment');
|
|
$table_name = 'migrate_example_wine_table_dest';
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('fooid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationTable::getKeySchema($table_name)
|
|
);
|
|
$query = db_select('migrate_example_wine_table_source', 't')
|
|
->fields('t', array('fooid', 'field1', 'field2'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationTable($table_name);
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('drupal_text', 'field1');
|
|
$this->addFieldMapping('drupal_int', 'field2');
|
|
|
|
$this->addUnmigratedDestinations(array('recordid'));
|
|
$this->removeFieldMapping('path');
|
|
$this->removeFieldMapping('pathauto');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This migration works with WinePrepMigration to make ensure auto_nodetitle
|
|
* is re-enabled if we disabled it.
|
|
*/
|
|
class WineFinishMigration extends MigrationBase {
|
|
public function __construct() {
|
|
parent::__construct(MigrateGroup::getInstance('wine', array('default')));
|
|
$this->description = t('If auto_nodetitle is present and was previously enabled,
|
|
re-enable it');
|
|
$this->dependencies = array('WineComment');
|
|
}
|
|
public function isComplete() {
|
|
if (module_exists('auto_nodetitle')) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
public function import() {
|
|
if (!module_exists('auto_nodetitle')) {
|
|
if (WinePrepMigration::$wasEnabled) {
|
|
module_enable(array('auto_nodetitle'));
|
|
self::displayMessage(t('Re-enabled auto_nodetitle module'), 'success');
|
|
}
|
|
else {
|
|
self::displayMessage(t('auto_nodetitle was not originally enabled'), 'success');
|
|
}
|
|
}
|
|
else {
|
|
self::displayMessage(t('Auto_nodetitle module already enabled'), 'success');
|
|
}
|
|
return Migration::RESULT_COMPLETED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TIP: This demonstrates a migration designed not to import new content, but
|
|
* to update existing content (in this case, revised wine ratings)
|
|
*/
|
|
class WineUpdatesMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Update wine ratings');
|
|
$this->dependencies = array('WineWine');
|
|
$this->softDependencies = array('WineFinish');
|
|
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'wineid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Wine ID',
|
|
'alias' => 'w',
|
|
)
|
|
),
|
|
MigrateDestinationNode::getKeySchema()
|
|
);
|
|
|
|
$query = db_select('migrate_example_wine_updates', 'w')
|
|
->fields('w', array('wineid', 'rating'));
|
|
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationNode('migrate_example_wine');
|
|
|
|
// Indicate we're updating existing data. The default, Migration::SOURCE, would
|
|
// cause existing nodes to be completely replaced by the source data. In this
|
|
// case, the existing node will be loaded and only the rating altered.
|
|
$this->systemOfRecord = Migration::DESTINATION;
|
|
|
|
// Mapped fields
|
|
// The destination handler needs the nid to change - since the incoming data
|
|
// has a source id, not a nid, we need to apply the original wine migration
|
|
// mapping to populate the nid.
|
|
$this->addFieldMapping('nid', 'wineid')
|
|
->sourceMigration('WineWine');
|
|
$this->addFieldMapping('field_migrate_example_wine_ratin', 'rating');
|
|
|
|
// No unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addFieldMapping('uid');
|
|
$this->addFieldMapping('migrate_example_wine_varieties');
|
|
$this->addFieldMapping('migrate_example_wine_regions');
|
|
$this->addFieldMapping('migrate_example_wine_best_with');
|
|
$this->addFieldMapping('body');
|
|
$this->addFieldMapping('field_migrate_example_image');
|
|
$this->addFieldMapping('sticky');
|
|
$this->addFieldMapping('created');
|
|
$this->addFieldMapping('changed');
|
|
$this->addUnmigratedDestinations(array('title', 'revision_uid', 'status', 'promote',
|
|
'revision', 'log', 'language', 'tnid', 'is_new', 'body:format', 'body:summary',
|
|
'body:language', 'migrate_example_wine_regions:source_type',
|
|
'migrate_example_wine_regions:create_term', 'comment',
|
|
'migrate_example_wine_varieties:source_type', 'migrate_example_wine_varieties:create_term',
|
|
'migrate_example_wine_best_with:source_type', 'migrate_example_wine_best_with:create_term',
|
|
'field_migrate_example_image:language', 'field_migrate_example_image:destination_dir',
|
|
'field_migrate_example_image:alt', 'field_migrate_example_image:title',
|
|
'field_migrate_example_image:file_class', 'field_migrate_example_image:file_replace',
|
|
'field_migrate_example_image:preserve_files', 'field_migrate_example_image:destination_file',
|
|
'field_migrate_example_image:source_dir', 'field_migrate_example_top_vintag'));
|
|
if (module_exists('statistics')) {
|
|
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
|
}
|
|
}
|
|
}
|
|
|
|
class WineCommentUpdatesMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = 'Update wine comments';
|
|
$this->dependencies = array('WineComment');
|
|
$this->softDependencies = array('WineUpdates');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('commentid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationComment::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_comment_updates', 'wc')
|
|
->fields('wc', array('commentid', 'subject'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationComment('comment_node_migrate_example_wine');
|
|
$this->systemOfRecord = Migration::DESTINATION;
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('cid', 'commentid')
|
|
->sourceMigration('WineComment');
|
|
$this->addFieldMapping('subject', 'subject');
|
|
|
|
// No unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addFieldMapping('name');
|
|
$this->addFieldMapping('mail');
|
|
$this->addFieldMapping('status');
|
|
$this->addFieldMapping('nid');
|
|
$this->addFieldMapping('uid');
|
|
$this->addFieldMapping('pid');
|
|
$this->addFieldMapping('comment_body');
|
|
$this->addFieldMapping('hostname');
|
|
$this->addFieldMapping('created');
|
|
$this->addFieldMapping('changed');
|
|
$this->addFieldMapping('homepage');
|
|
$this->addUnmigratedDestinations(array('thread', 'language',
|
|
'comment_body:format', 'comment_body:language'));
|
|
$this->removeFieldMapping('pathauto');
|
|
}
|
|
}
|
|
|
|
class WineVarietyUpdatesMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Migrate varieties from the source database to taxonomy terms');
|
|
$this->dependencies = array('WineVariety');
|
|
$this->softDependencies = array('WineUpdates');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array(
|
|
'categoryid' => array('type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
)
|
|
),
|
|
MigrateDestinationTerm::getKeySchema()
|
|
);
|
|
|
|
$query = db_select('migrate_example_wine_variety_updates', 'wc')
|
|
->fields('wc', array('categoryid', 'details'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationTerm('migrate_example_wine_varieties');
|
|
$this->systemOfRecord = Migration::DESTINATION;
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('tid', 'categoryid')
|
|
->sourceMigration('WineVariety');
|
|
$this->addFieldMapping('description', 'details');
|
|
|
|
// Unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addFieldMapping('name');
|
|
$this->addFieldMapping('parent');
|
|
$this->addFieldMapping('weight');
|
|
$this->addFieldMapping('format');
|
|
$this->addFieldMapping('parent_name')
|
|
->issueGroup(t('DNM'));
|
|
}
|
|
}
|
|
|
|
|
|
class WineUserUpdatesMigration extends AdvancedExampleMigration {
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->description = t('Account updates');
|
|
$this->dependencies = array('WineUser');
|
|
$this->softDependencies = array('WineUpdates');
|
|
$this->map = new MigrateSQLMap($this->machineName,
|
|
array('accountid' => array(
|
|
'type' => 'int',
|
|
'unsigned' => TRUE,
|
|
'not null' => TRUE,
|
|
'description' => 'Account ID.'
|
|
)
|
|
),
|
|
MigrateDestinationUser::getKeySchema()
|
|
);
|
|
$query = db_select('migrate_example_wine_account_updates', 'wa')
|
|
->fields('wa', array('accountid', 'sex'));
|
|
$this->source = new MigrateSourceSQL($query);
|
|
$this->destination = new MigrateDestinationUser();
|
|
$this->systemOfRecord = Migration::DESTINATION;
|
|
|
|
// Mapped fields
|
|
$this->addFieldMapping('uid', 'accountid')
|
|
->sourceMigration('WineUser');
|
|
$this->addFieldMapping('field_migrate_example_gender', 'sex')
|
|
->description(t('Map from M/F to 0/1 in prepare method'));
|
|
|
|
// Unmapped source fields
|
|
|
|
// Unmapped destination fields
|
|
$this->addFieldMapping('name');
|
|
$this->addFieldMapping('status');
|
|
$this->addFieldMapping('created');
|
|
$this->addFieldMapping('access');
|
|
$this->addFieldMapping('login');
|
|
$this->addFieldMapping('mail');
|
|
$this->addFieldMapping('pass');
|
|
$this->addFieldMapping('roles');
|
|
$this->addFieldMapping('signature');
|
|
$this->addFieldMapping('signature_format');
|
|
$this->addFieldMapping('init');
|
|
$this->addUnmigratedDestinations(array('theme', 'timezone', 'language',
|
|
'picture', 'is_new', 'field_migrate_example_favbeers'));
|
|
}
|
|
|
|
public function prepare(stdClass $account, stdClass $row) {
|
|
// Gender data comes in as M/F, needs to be saved as Male=0/Female=1
|
|
// TIP: Note that the Migration prepare method is called after all other
|
|
// prepare handlers. Most notably, the field handlers have had their way
|
|
// and created field arrays, so we have to save in the same format.
|
|
switch ($row->sex) {
|
|
case 'm':
|
|
case 'M':
|
|
$account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 0;
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
$account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 1;
|
|
break;
|
|
default:
|
|
$account->field_migrate_example_gender = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|