wine.inc 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249
  1. <?php
  2. /**
  3. * @file
  4. * Advanced migration examples. These serve two purposes:
  5. *
  6. * 1. To demonstrate some of the more advanced usages of the Migrate module.
  7. * Search for "TIP:" below for features not found in the basic example.
  8. * 2. To provide thorough test cases for the simpletest suite.
  9. *
  10. */
  11. /**
  12. * Abstract intermediate class holding common settings.
  13. */
  14. abstract class AdvancedExampleMigration extends DynamicMigration {
  15. public $basicFormat;
  16. public function __construct() {
  17. // TIP: Migrations can be organized into groups. In this case, all the migrations
  18. // derived from AdvancedExampleMigration will be part of the 'wine' group.
  19. // This enables us to easily run just the wine example migrations:
  20. // drush migrate-import --group=wine
  21. // The second argument to MigrateGroup::getInstance is an array of groups
  22. // which should come before this when viewing migration statuses, or running
  23. // migration operations using --all. Since the beer migrations in this module
  24. // did not specify a group, it is in the 'default' group, so this constructor
  25. // indicates that the wine migrations come after the beer migrations.
  26. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  27. $this->team = array(
  28. new MigrateTeamMember('Jack Kramer', 'jkramer@example.com', t('Taster')),
  29. new MigrateTeamMember('Linda Madison', 'lmadison@example.com', t('Winemaker')),
  30. );
  31. $this->issuePattern = 'http://drupal.org/node/:id:';
  32. // A format of our own, for testing migration of formats
  33. $this->basicFormat = filter_format_load('migrate_example');
  34. // We can do shared field mappings in the common class
  35. if (module_exists('path')) {
  36. $this->addFieldMapping('path')
  37. ->issueGroup(t('DNM'));
  38. if (module_exists('pathauto')) {
  39. $this->addFieldMapping('pathauto')
  40. ->issueGroup(t('DNM'));
  41. }
  42. }
  43. }
  44. }
  45. /**
  46. * TIP: While usually you'll create true migrations - processes that copy data
  47. * from some source into Drupal - you can also define processing steps for either
  48. * the import or rollback stages that take other actions. In this case, we want
  49. * to disable auto_nodetitle while the migration steps run. We'll re-enable it
  50. * over in WineFinishMigration.
  51. */
  52. class WinePrepMigration extends MigrationBase {
  53. // Track whether the auto_nodetitle was originally enabled so we know whether
  54. // to re-enable it. This is public so WineFinishMigration can reference it.
  55. public static $wasEnabled = FALSE;
  56. public function __construct() {
  57. // Because we're derived directly from migrationBase rather than AdvancedExampleMigration,
  58. // we must specify the group again here.
  59. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  60. $this->description = t('If auto_nodetitle is present, disable it for the duration');
  61. }
  62. // Define isComplete(), returning a boolean, to indicate whether dependent
  63. // migrations may proceed
  64. public function isComplete() {
  65. // If Auto Node Title is disabled, other migrations are free to go
  66. if (module_exists('auto_nodetitle')) {
  67. return FALSE;
  68. }
  69. else {
  70. return TRUE;
  71. }
  72. }
  73. // Implement any action you want to occur during an import process in an
  74. // import() method (alternatively, if you have an action which you want to
  75. // run during rollbacks, define a rollback() method).
  76. public function import() {
  77. if (module_exists('auto_nodetitle')) {
  78. self::$wasEnabled = TRUE;
  79. module_disable(array('auto_nodetitle'));
  80. self::displayMessage(t('Disabled auto_nodetitle module'), 'success');
  81. }
  82. else {
  83. self::$wasEnabled = FALSE;
  84. self::displayMessage(t('Auto_nodetitle is already disabled'), 'success');
  85. }
  86. // Must return one of the MigrationBase RESULT constants
  87. return MigrationBase::RESULT_COMPLETED;
  88. }
  89. }
  90. // The term migrations are very similar - implement the commonalities here
  91. abstract class WineTermMigration extends AdvancedExampleMigration {
  92. public function __construct($type, $vocabulary_name, $description) {
  93. parent::__construct();
  94. $this->description = $description;
  95. $this->dependencies = array('WinePrep');
  96. $this->map = new MigrateSQLMap($this->machineName,
  97. array(
  98. 'categoryid' => array('type' => 'int',
  99. 'unsigned' => TRUE,
  100. 'not null' => TRUE,
  101. )
  102. ),
  103. MigrateDestinationTerm::getKeySchema()
  104. );
  105. $query = db_select('migrate_example_wine_categories', 'wc')
  106. ->fields('wc', array('categoryid', 'name', 'details', 'category_parent', 'ordering'))
  107. ->condition('type', $type)
  108. // This sort assures that parents are saved before children.
  109. ->orderBy('category_parent', 'ASC');
  110. $this->source = new MigrateSourceSQL($query);
  111. $this->destination = new MigrateDestinationTerm($vocabulary_name);
  112. // Mapped fields
  113. $this->addFieldMapping('name', 'name');
  114. $this->addFieldMapping('description', 'details');
  115. $this->addFieldMapping('parent', 'category_parent')
  116. ->sourceMigration($this->getMachineName());
  117. $this->addFieldMapping('weight', 'ordering');
  118. $this->addFieldMapping('format')
  119. ->defaultValue($this->basicFormat->format);
  120. // Unmapped source fields
  121. // Unmapped destination fields
  122. $this->addFieldMapping('parent_name')
  123. ->issueGroup(t('DNM'));
  124. }
  125. }
  126. class WineVarietyMigration extends WineTermMigration {
  127. public function __construct() {
  128. parent::__construct('variety', 'migrate_example_wine_varieties',
  129. t('Migrate varieties from the source database to taxonomy terms'));
  130. }
  131. }
  132. class WineRegionMigration extends WineTermMigration {
  133. public function __construct() {
  134. parent::__construct('region', 'migrate_example_wine_regions',
  135. t('Migrate regions from the source database to taxonomy terms'));
  136. }
  137. }
  138. class WineBestWithMigration extends WineTermMigration {
  139. public function __construct() {
  140. parent::__construct('best_with', 'migrate_example_wine_best_with',
  141. t('Migrate "Best With" from the source database to taxonomy terms'));
  142. }
  143. }
  144. /**
  145. * TIP: Files can be migrated directly by themselves, by using the MigrateDestinationFile
  146. * class. This will copy the files themselves from the source, and set up the
  147. * Drupal file tables appropriately.
  148. */
  149. class WineFileCopyMigration extends AdvancedExampleMigration {
  150. public function __construct() {
  151. parent::__construct();
  152. $this->description = t('Profile images');
  153. $this->dependencies = array('WinePrep');
  154. $this->map = new MigrateSQLMap($this->machineName,
  155. array('imageid' => array(
  156. 'type' => 'int',
  157. 'unsigned' => TRUE,
  158. 'not null' => TRUE,
  159. 'description' => 'Image ID.'
  160. )
  161. ),
  162. MigrateDestinationFile::getKeySchema()
  163. );
  164. $query = db_select('migrate_example_wine_files', 'wf')
  165. ->fields('wf', array('imageid', 'url'))
  166. ->isNull('wineid');
  167. $this->source = new MigrateSourceSQL($query);
  168. $this->destination = new MigrateDestinationFile();
  169. // In the simplest case, just map the incoming URL to 'value'.
  170. $this->addFieldMapping('value', 'url');
  171. $this->addUnmigratedDestinations(array('fid', 'uid', 'timestamp',
  172. 'destination_dir', 'destination_file', 'source_dir', 'preserve_files',
  173. 'file_replace'));
  174. $this->removeFieldMapping('path');
  175. $this->removeFieldMapping('pathauto');
  176. }
  177. }
  178. /**
  179. * Migration class to test importing from a BLOB column into a file entity.
  180. * Typically, one would use this OR import into a File Field.
  181. * @see MigrateExampleOracleNode()
  182. */
  183. class WineFileBlobMigration extends AdvancedExampleMigration {
  184. public function __construct() {
  185. parent::__construct();
  186. $this->description = t('Example migration from BLOB column into files.');
  187. $this->dependencies = array('WinePrep');
  188. $this->map = new MigrateSQLMap($this->machineName,
  189. array('imageid' => array(
  190. 'type' => 'int',
  191. 'unsigned' => TRUE,
  192. 'not null' => TRUE,
  193. 'description' => 'Image ID.'
  194. )
  195. ),
  196. MigrateDestinationFile::getKeySchema()
  197. );
  198. $query = db_select('migrate_example_wine_blobs', 'wf')
  199. ->fields('wf', array('imageid', 'imageblob'));
  200. $this->source = new MigrateSourceSQL($query);
  201. // Note that the WineFileCopyMigration example let the second argument,
  202. // the file_class, to default to MigrateFileUri, indicating that the
  203. // 'value' we're passing was a URI. In this case, we're passing a blob, so
  204. // tell the destination to expect it.
  205. $this->destination = new MigrateDestinationFile('file', 'MigrateFileBlob');
  206. // Basic fields
  207. $this->addFieldMapping('uid')
  208. ->defaultValue(1);
  209. // The destination filename must be specified for blobs
  210. $this->addFieldMapping('destination_file')
  211. ->defaultValue('druplicon.png');
  212. $this->addFieldMapping('value', 'imageblob')
  213. ->description('An image blob in the DB');
  214. // Unmapped destination fields
  215. $this->addUnmigratedDestinations(array('fid', 'timestamp',
  216. 'destination_dir', 'file_replace', 'preserve_files'));
  217. // Our base class mapped these since most migrations use them, but not this
  218. // one, so remove them
  219. $this->removeFieldMapping('path');
  220. $this->removeFieldMapping('pathauto');
  221. }
  222. }
  223. class WineRoleMigration extends XMLMigration {
  224. public function __construct() {
  225. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  226. $this->description = t('XML feed (multi items) of roles (positions)');
  227. // TIP: Regular dependencies, besides enforcing (in the absence of --force)
  228. // the run order of migrations, affect the sorting of migrations on display.
  229. // You can use soft dependencies to affect just the display order when the
  230. // migrations aren't technically required to run in a certain order. In this
  231. // case, we want the role migration to appear after the file migration.
  232. $this->softDependencies = array('WineFileCopy');
  233. // There isn't a consistent way to automatically identify appropriate "fields"
  234. // from an XML feed, so we pass an explicit list of source fields
  235. $fields = array(
  236. 'name' => t('Position name'),
  237. );
  238. // The source ID here is the one retrieved from each data item in the XML file, and
  239. // used to identify specific items
  240. $this->map = new MigrateSQLMap($this->machineName,
  241. array(
  242. 'sourceid' => array(
  243. 'type' => 'int',
  244. 'unsigned' => TRUE,
  245. 'not null' => TRUE,
  246. )
  247. ),
  248. MigrateDestinationRole::getKeySchema()
  249. );
  250. // IMPORTANT: Do not try this at home! We have included importable files
  251. // with the migrate_example module so it can be very simply installed and
  252. // run, but you should never include any data you want to keep private
  253. // (especially user data like email addresses, phone numbers, etc.) in the
  254. // module directory. Your source data should be outside of the webroot, and
  255. // should not be anywhere where it may get committed into a revision control
  256. // system.
  257. // This can also be an URL instead of a file path.
  258. $xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
  259. $items_url = $xml_folder . 'positions.xml';
  260. $item_xpath = '/positions/position'; // relative to document
  261. $item_ID_xpath = 'sourceid'; // relative to item_xpath and gets assembled
  262. // into full path /producers/producer/sourceid
  263. $items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
  264. $this->source = new MigrateSourceMultiItems($items_class, $fields);
  265. $this->destination = new MigrateDestinationRole();
  266. $this->addFieldMapping('name', 'name')
  267. ->xpath('name');
  268. $this->addUnmigratedDestinations(array('weight'));
  269. }
  270. }
  271. class WineUserMigration extends AdvancedExampleMigration {
  272. public function __construct() {
  273. parent::__construct();
  274. $this->description = t('Wine Drinkers of the world');
  275. $this->dependencies = array('WinePrep', 'WineFileCopy', 'WineRole');
  276. $this->map = new MigrateSQLMap($this->machineName,
  277. array('accountid' => array(
  278. 'type' => 'int',
  279. 'unsigned' => TRUE,
  280. 'not null' => TRUE,
  281. 'description' => 'Account ID.'
  282. )
  283. ),
  284. MigrateDestinationUser::getKeySchema()
  285. );
  286. $query = db_select('migrate_example_wine_account', 'wa')
  287. ->fields('wa', array('accountid', 'status', 'posted', 'name',
  288. 'password', 'mail', 'last_access', 'last_login',
  289. 'original_mail', 'sig', 'sex', 'imageid', 'positions'));
  290. $this->source = new MigrateSourceSQL($query);
  291. $this->destination = new MigrateDestinationUser();
  292. // Mapped fields
  293. $this->addSimpleMappings(array('name', 'status', 'mail'));
  294. $this->addFieldMapping('created', 'posted')
  295. ->description('See prepare method');
  296. $this->addFieldMapping('access', 'last_access')
  297. ->description('See prepare method');
  298. $this->addFieldMapping('login', 'last_login')
  299. ->description('See prepare method');
  300. $this->addFieldMapping('pass', 'password');
  301. $this->addFieldMapping('roles', 'positions')
  302. ->separator(',')
  303. ->sourceMigration('WineRole');
  304. $this->addFieldMapping('signature', 'sig');
  305. $this->addFieldMapping('signature_format')
  306. ->defaultValue($this->basicFormat->format);
  307. $this->addFieldMapping('init', 'original_mail');
  308. $this->addFieldMapping('field_migrate_example_gender', 'sex')
  309. ->description(t('Map from M/F to 0/1 in prepare method'));
  310. $this->addFieldMapping('picture', 'imageid')
  311. ->sourceMigration('WineFileCopy');
  312. // Unmapped source fields
  313. // Unmapped destination fields
  314. $this->addUnmigratedDestinations(array('theme', 'timezone', 'language',
  315. 'is_new', 'field_migrate_example_favbeers', 'role_names', 'data'));
  316. }
  317. public function prepare(stdClass $account, stdClass $row) {
  318. // Source dates are in ISO format.
  319. // Because the mappings above have been applied, $account->created contains
  320. // the date/time string now - we could also pass $row->posted here.
  321. $account->created = strtotime($account->created);
  322. $account->access = strtotime($account->access);
  323. $account->login = strtotime($account->login);
  324. // Gender data comes in as M/F, needs to be saved as Male=0/Female=1
  325. // TIP: Note that the Migration prepare method is called after all other
  326. // prepare handlers. Most notably, the field handlers have had their way
  327. // and created field arrays, so we have to save in the same format.
  328. switch ($row->sex) {
  329. case 'm':
  330. case 'M':
  331. $account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 0;
  332. break;
  333. case 'f':
  334. case 'F':
  335. $account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 1;
  336. break;
  337. default:
  338. unset($account->field_migrate_example_gender);
  339. break;
  340. }
  341. }
  342. }
  343. class WineProducerMigration extends AdvancedExampleMigration {
  344. public function __construct() {
  345. parent::__construct();
  346. $this->description = t('Wine producers of the world');
  347. $this->dependencies = array('WineRegion', 'WineUser');
  348. $this->map = new MigrateSQLMap($this->machineName,
  349. array(
  350. 'producerid' => array(
  351. 'type' => 'int',
  352. 'unsigned' => TRUE,
  353. 'not null' => TRUE,
  354. 'alias' => 'p',
  355. )
  356. ),
  357. MigrateDestinationNode::getKeySchema()
  358. );
  359. $query = db_select('migrate_example_wine_producer', 'p')
  360. ->fields('p', array('producerid', 'name', 'body', 'excerpt', 'accountid'));
  361. // Region term is singletons, handled straighforwardly
  362. $query->leftJoin('migrate_example_wine_category_producer', 'reg',
  363. "p.producerid = reg.producerid");
  364. $query->addField('reg', 'categoryid', 'region');
  365. $this->source = new MigrateSourceSQL($query);
  366. $this->destination = new MigrateDestinationNode('migrate_example_producer');
  367. // Mapped fields
  368. $this->addFieldMapping('title', 'name')
  369. ->description(t('Mapping producer name in source to node title'));
  370. $this->addFieldMapping('uid', 'accountid')
  371. ->sourceMigration('WineUser')
  372. ->defaultValue(1);
  373. $this->addFieldMapping('migrate_example_wine_regions', 'region')
  374. ->sourceMigration('WineRegion');
  375. $this->addFieldMapping('migrate_example_wine_regions:source_type')
  376. ->defaultValue('tid');
  377. $this->addFieldMapping('body', 'body');
  378. $this->addFieldMapping('body:summary', 'excerpt');
  379. $this->addFieldMapping('sticky')
  380. ->defaultValue(0);
  381. // No unmapped source fields
  382. // Unmapped destination fields
  383. $this->addUnmigratedDestinations(array('is_new', 'created', 'changed',
  384. 'status', 'promote', 'revision', 'language', 'revision_uid', 'log', 'tnid',
  385. 'body:format', 'body:language', 'migrate_example_wine_regions:create_term',
  386. 'comment'));
  387. if (module_exists('statistics')) {
  388. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  389. }
  390. }
  391. }
  392. /**
  393. * TIP: An example of importing from an XML feed. See the files in the xml
  394. * directory - index.xml contains a list of IDs to import, and <id>.xml
  395. * is the data for a given producer.
  396. *
  397. * Note that, if basing a migration on an XML source, you need to derive it
  398. * from XMLMigration instead of Migration.
  399. */
  400. class WineProducerXMLMigration extends XMLMigration {
  401. public function __construct() {
  402. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  403. $this->description = t('XML feed of wine producers of the world');
  404. $this->dependencies = array('WineRegion', 'WineUser');
  405. // There isn't a consistent way to automatically identify appropriate "fields"
  406. // from an XML feed, so we pass an explicit list of source fields
  407. $fields = array(
  408. 'name' => t('Producer name'),
  409. 'description' => t('Description of producer'),
  410. 'authorid' => t('Numeric ID of the author'),
  411. 'region' => t('Name of region'),
  412. );
  413. // The source ID here is the one retrieved from the XML listing file, and
  414. // used to identify the specific item's file
  415. $this->map = new MigrateSQLMap($this->machineName,
  416. array(
  417. 'sourceid' => array(
  418. 'type' => 'varchar',
  419. 'length' => 4,
  420. 'not null' => TRUE,
  421. )
  422. ),
  423. MigrateDestinationNode::getKeySchema()
  424. );
  425. // IMPORTANT: Do not try this at home! We have included importable files
  426. // with the migrate_example module so it can be very simply installed and
  427. // run, but you should never include any data you want to keep private
  428. // (especially user data like email addresses, phone numbers, etc.) in the
  429. // module directory. Your source data should be outside of the webroot, and
  430. // should not be anywhere where it may get committed into a revision control
  431. // system.
  432. // This can also be an URL instead of a file path.
  433. $xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
  434. $list_url = $xml_folder . 'index.xml';
  435. // Each ID retrieved from the list URL will be plugged into :id in the
  436. // item URL to fetch the specific objects.
  437. $item_url = $xml_folder . ':id.xml';
  438. // We use the MigrateSourceList class for any source where we obtain the list
  439. // of IDs to process separately from the data for each item. The listing
  440. // and item are represented by separate classes, so for example we could
  441. // replace the XML listing with a file directory listing, or the XML item
  442. // with a JSON item.
  443. $this->source = new MigrateSourceList(new MigrateListXML($list_url),
  444. new MigrateItemXML($item_url), $fields);
  445. $this->destination = new MigrateDestinationNode('migrate_example_producer');
  446. // TIP: Note that for XML sources, in addition to the source field passed to
  447. // addFieldMapping (the name under which it will be saved in the data row
  448. // passed through the migration process) we specify the Xpath used to retrieve
  449. // the value from the XML.
  450. $this->addFieldMapping('title', 'name')
  451. ->xpath('/producer/name');
  452. $this->addFieldMapping('uid', 'authorid')
  453. ->xpath('/producer/authorid')
  454. ->sourceMigration('WineUser')
  455. ->defaultValue(1);
  456. $this->addFieldMapping('migrate_example_wine_regions', 'region')
  457. ->xpath('/producer/region');
  458. $this->addFieldMapping('body', 'description')
  459. ->xpath('/producer/description');
  460. $this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
  461. 'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
  462. 'is_new', 'body:summary', 'body:format', 'body:language',
  463. 'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
  464. 'comment'));
  465. if (module_exists('path')) {
  466. $this->addFieldMapping('path')
  467. ->issueGroup(t('DNM'));
  468. if (module_exists('pathauto')) {
  469. $this->addFieldMapping('pathauto')
  470. ->issueGroup(t('DNM'));
  471. }
  472. }
  473. if (module_exists('statistics')) {
  474. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  475. }
  476. }
  477. }
  478. /**
  479. * TIP: An example of importing from an XML feed where both the id and the
  480. * data to import are in the same file. The id is a part of the data. See
  481. * the file in the xml directory - producers.xml which contains all IDs and
  482. * producer data for this example.
  483. *
  484. * Note that, if basing a migration on an XML source, you need to derive it
  485. * from XMLMigration instead of Migration.
  486. */
  487. class WineProducerMultiXMLMigration extends XMLMigration {
  488. public function __construct() {
  489. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  490. $this->description = t('XML feed (multi items) of wine producers of the world');
  491. $this->dependencies = array('WineRegion', 'WineUser');
  492. // There isn't a consistent way to automatically identify appropriate "fields"
  493. // from an XML feed, so we pass an explicit list of source fields
  494. $fields = array(
  495. 'name' => t('Producer name'),
  496. 'description' => t('Description of producer'),
  497. 'authorid' => t('Numeric ID of the author'),
  498. 'region' => t('Name of region'),
  499. );
  500. // The source ID here is the one retrieved from each data item in the XML file, and
  501. // used to identify specific items
  502. $this->map = new MigrateSQLMap($this->machineName,
  503. array(
  504. 'sourceid' => array(
  505. 'type' => 'varchar',
  506. 'length' => 4,
  507. 'not null' => TRUE,
  508. )
  509. ),
  510. MigrateDestinationNode::getKeySchema()
  511. );
  512. // IMPORTANT: Do not try this at home! We have included importable files
  513. // with the migrate_example module so it can be very simply installed and
  514. // run, but you should never include any data you want to keep private
  515. // (especially user data like email addresses, phone numbers, etc.) in the
  516. // module directory. Your source data should be outside of the webroot, and
  517. // should not be anywhere where it may get committed into a revision control
  518. // system.
  519. // This can also be an URL instead of a file path.
  520. $xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
  521. $items_url = $xml_folder . 'producers.xml';
  522. // We use the MigrateSourceMultiItems class for any source where we obtain the list
  523. // of IDs to process and the data for each item from the same file. Typically the data
  524. // for an item is not contained in a single line within the source file. Examples include
  525. // multiple items defined in a single xml file or a single json file where in both cases
  526. // the id is part of the item.
  527. $item_xpath = '/producers/producer'; // relative to document
  528. $item_ID_xpath = 'sourceid'; // relative to item_xpath and gets assembled
  529. // into full path /producers/producer/sourceid
  530. $items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
  531. $this->source = new MigrateSourceMultiItems($items_class, $fields);
  532. $this->destination = new MigrateDestinationNode('migrate_example_producer');
  533. // TIP: Note that for XML sources, in addition to the source field passed to
  534. // addFieldMapping (the name under which it will be saved in the data row
  535. // passed through the migration process) we specify the Xpath used to retrieve
  536. // the value from the XML.
  537. // TIP: Note that all xpaths for fields begin at the last element of the item
  538. // xpath since each item xml chunk is processed individually.
  539. // (ex. xpath=name is equivalent to a full xpath of /producers/producer/name)
  540. $this->addFieldMapping('title', 'name')
  541. ->xpath('name');
  542. $this->addFieldMapping('uid', 'authorid')
  543. ->xpath('authorid')
  544. ->sourceMigration('WineUser')
  545. ->defaultValue(1);
  546. $this->addFieldMapping('migrate_example_wine_regions', 'region')
  547. ->xpath('region');
  548. $this->addFieldMapping('body', 'description')
  549. ->xpath('description');
  550. $this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
  551. 'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
  552. 'is_new', 'body:summary', 'body:format', 'body:language',
  553. 'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
  554. 'comment'));
  555. if (module_exists('path')) {
  556. $this->addFieldMapping('path')
  557. ->issueGroup(t('DNM'));
  558. if (module_exists('pathauto')) {
  559. $this->addFieldMapping('pathauto')
  560. ->issueGroup(t('DNM'));
  561. }
  562. }
  563. if (module_exists('statistics')) {
  564. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  565. }
  566. }
  567. }
  568. /**
  569. * TIP: An alternative approach using MigrateSourceSQL. This uses a different
  570. * XML library, which advances element-by-element through the XML file rather
  571. * than reading in the whole file. This source will work better with large XML
  572. * files, but is slower for small files and has a more restrictive query lanaguage
  573. * for selecting the elements to process.
  574. */
  575. class WineProducerXMLPullMigration extends XMLMigration {
  576. public function __construct() {
  577. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  578. $this->description = t('XML feed (pull) of wine producers of the world');
  579. $this->dependencies = array('WineRegion', 'WineUser');
  580. $fields = array(
  581. 'name' => t('Producer name'),
  582. 'description' => t('Description of producer'),
  583. 'authorid' => t('Numeric ID of the author'),
  584. 'region' => t('Name of region'),
  585. );
  586. $this->map = new MigrateSQLMap($this->machineName,
  587. array(
  588. 'sourceid' => array(
  589. 'type' => 'varchar',
  590. 'length' => 4,
  591. 'not null' => TRUE,
  592. )
  593. ),
  594. MigrateDestinationNode::getKeySchema()
  595. );
  596. // IMPORTANT: Do not try this at home! We have included importable files
  597. // with the migrate_example module so it can be very simply installed and
  598. // run, but you should never include any data you want to keep private
  599. // (especially user data like email addresses, phone numbers, etc.) in the
  600. // module directory. Your source data should be outside of the webroot, and
  601. // should not be anywhere where it may get committed into a revision control
  602. // system.
  603. // This can also be an URL instead of a file path.
  604. $xml_folder = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_example') . '/xml/';
  605. $items_url = $xml_folder . 'producers2.xml';
  606. // As with MigrateSourceMultiItems, this applies where there is not a separate
  607. // list of IDs to process - the source XML file is entirely self-contained.
  608. // For the ID path, and xpath for each component, we can use the full xpath
  609. // syntax as usual. However, the syntax to select the elements that correspond
  610. // to objects to import is more limited. It must be a fully-qualified path
  611. // to the element (i.e., /producers/producer rather than just //producer).
  612. $item_xpath = '/producers/producer'; // relative to document
  613. $item_ID_xpath = 'sourceid'; // relative to item_xpath
  614. $this->source = new MigrateSourceXML($items_url, $item_xpath, $item_ID_xpath,
  615. $fields);
  616. $this->destination = new MigrateDestinationNode('migrate_example_producer');
  617. $this->addFieldMapping('title', 'name')
  618. ->xpath('name');
  619. $this->addFieldMapping('uid', 'authorid')
  620. ->xpath('authorid')
  621. ->sourceMigration('WineUser')
  622. ->defaultValue(1);
  623. $this->addFieldMapping('migrate_example_wine_regions', 'region')
  624. ->xpath('region');
  625. $this->addFieldMapping('body', 'description')
  626. ->xpath('description');
  627. $this->addUnmigratedDestinations(array('revision_uid', 'created', 'changed',
  628. 'status', 'promote', 'sticky', 'revision', 'log', 'language', 'tnid',
  629. 'is_new', 'body:summary', 'body:format', 'body:language',
  630. 'migrate_example_wine_regions:source_type', 'migrate_example_wine_regions:create_term',
  631. 'comment'));
  632. if (module_exists('path')) {
  633. $this->addFieldMapping('path')
  634. ->issueGroup(t('DNM'));
  635. if (module_exists('pathauto')) {
  636. $this->addFieldMapping('pathauto')
  637. ->issueGroup(t('DNM'));
  638. }
  639. }
  640. if (module_exists('statistics')) {
  641. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  642. }
  643. }
  644. }
  645. // TODO: Add node_reference field pointing to producer
  646. class WineWineMigration extends AdvancedExampleMigration {
  647. public function __construct() {
  648. parent::__construct();
  649. $this->description = t('Wines of the world');
  650. $this->dependencies = array('WineVariety', 'WineRegion',
  651. 'WineBestWith', 'WineUser', 'WineProducer');
  652. // You can add a 'track_last_imported' option to the map, to record the
  653. // timestamp of when each item was last imported in the map table.
  654. $this->map = new MigrateSQLMap($this->machineName,
  655. array(
  656. 'wineid' => array(
  657. 'type' => 'int',
  658. 'unsigned' => TRUE,
  659. 'not null' => TRUE,
  660. 'description' => 'Wine ID',
  661. 'alias' => 'w',
  662. )
  663. ),
  664. MigrateDestinationNode::getKeySchema(),
  665. 'default',
  666. array('track_last_imported' => TRUE)
  667. );
  668. $query = db_select('migrate_example_wine', 'w')
  669. ->fields('w', array('wineid', 'name', 'body', 'excerpt', 'accountid',
  670. 'posted', 'last_changed', 'variety', 'region', 'rating'));
  671. $query->leftJoin('migrate_example_wine_category_wine', 'cwbw',
  672. "w.wineid = cwbw.wineid");
  673. $query->leftJoin('migrate_example_wine_categories', 'bw',
  674. "cwbw.categoryid = bw.categoryid AND bw.type = 'best_with'");
  675. // Gives a single comma-separated list of related terms
  676. $query->groupBy('w.wineid');
  677. $query->addExpression('GROUP_CONCAT(bw.categoryid)', 'best_with');
  678. $count_query = db_select('migrate_example_wine', 'w');
  679. $count_query->addExpression('COUNT(wineid)', 'cnt');
  680. // TIP: By passing an array of source fields to the MigrateSourceSQL constructor,
  681. // we can modify the descriptions of source fields (which just default, for
  682. // SQL migrations, to table_alias.column_name), as well as add additional fields
  683. // (which may be populated in prepareRow()).
  684. $source_fields = array(
  685. 'wineid' => t('Wine ID in the old system'),
  686. 'name' => t('The name of the wine'),
  687. 'best_vintages' => t('What years were best for this wine?'),
  688. 'url' => t('Image URLs attached to this wine; populated in prepareRow()'),
  689. 'image_alt' => t('Image alt text attached to this wine; populated in prepareRow()'),
  690. 'image_title' => t('Image titles attached to this wine; populated in prepareRow()'),
  691. );
  692. // TIP: By default, each time a migration is run, any previously unimported source items
  693. // are imported (along with any previously-imported items marked for update). If the
  694. // source data contains a timestamp that is set to the creation time of each new item,
  695. // as well as set to the update time for any existing items that are updated, then
  696. // you can have those updated items automatically reimported by setting the field as
  697. // your highwater field.
  698. $this->highwaterField = array(
  699. 'name' => 'last_changed', // Column to be used as highwater mark
  700. 'alias' => 'w', // Table alias containing that column
  701. 'type' => 'int', // By default, highwater marks are assumed to be lexicographically
  702. // sortable (e.g., '2011-05-19 17:53:12'). To properly
  703. // deal with integer highwater marks (such as UNIX
  704. // timestamps), indicate so here.
  705. );
  706. // Note that it is important to process rows in the order of the highwater mark
  707. $query->orderBy('last_changed');
  708. $this->source = new MigrateSourceSQL($query, $source_fields, $count_query);
  709. $this->destination = new MigrateDestinationNode('migrate_example_wine');
  710. // Mapped fields
  711. $this->addFieldMapping('title', 'name')
  712. ->description(t('Mapping wine name in source to node title'));
  713. $this->addFieldMapping('uid', 'accountid')
  714. ->sourceMigration('WineUser')
  715. ->defaultValue(1);
  716. // TIP: By default, term relationship are assumed to be passed by name.
  717. // In this case, the source values are IDs, so we specify the relevant
  718. // migration (so the tid can be looked up in the map), and tell the term
  719. // field handler that it is receiving tids instead of names
  720. $this->addFieldMapping('migrate_example_wine_varieties', 'variety')
  721. ->sourceMigration('WineVariety');
  722. $this->addFieldMapping('migrate_example_wine_varieties:source_type')
  723. ->defaultValue('tid');
  724. $this->addFieldMapping('migrate_example_wine_regions', 'region')
  725. ->sourceMigration('WineRegion');
  726. $this->addFieldMapping('migrate_example_wine_regions:source_type')
  727. ->defaultValue('tid');
  728. $this->addFieldMapping('migrate_example_wine_best_with', 'best_with')
  729. ->separator(',')
  730. ->sourceMigration('WineBestWith');
  731. $this->addFieldMapping('migrate_example_wine_best_with:source_type')
  732. ->defaultValue('tid');
  733. $this->addFieldMapping('field_migrate_example_wine_ratin', 'rating');
  734. $this->addFieldMapping('field_migrate_example_top_vintag', 'best_vintages');
  735. // TIP: You can apply one or more functions to a source value using ->callbacks().
  736. // The function must take a single argument and return a value which is a
  737. // transformation of the argument. As this example shows, you can have multiple
  738. // callbacks, and they can either be straight functions or class methods. In
  739. // this case, our custom method prepends 'review: ' to the body, and then we
  740. // call a standard Drupal function to uppercase the whole body.
  741. $this->addFieldMapping('body', 'body')
  742. ->callbacks(array($this, 'addTitlePrefix'), 'drupal_strtoupper');
  743. $this->addFieldMapping('body:summary', 'excerpt');
  744. // We will get the image data from a related table in prepareRow()
  745. $this->addFieldMapping('field_migrate_example_image', 'url');
  746. // Indicate that we want each file to maintain its name, replacing any
  747. // previous file of the same name (as opposed to being renamed to avoid
  748. // collisions, which is the default).
  749. $this->addFieldMapping('field_migrate_example_image:file_replace')
  750. ->defaultValue(FILE_EXISTS_REPLACE);
  751. $this->addFieldMapping('field_migrate_example_image:alt', 'image_alt');
  752. $this->addFieldMapping('field_migrate_example_image:title', 'image_title');
  753. $this->addFieldMapping('sticky')
  754. ->defaultValue(0);
  755. // These are already UNIX timestamps, so just pass through
  756. $this->addFieldMapping('created', 'posted');
  757. $this->addFieldMapping('changed', 'last_changed');
  758. // No unmapped source fields
  759. // Unmapped destination fields
  760. $this->addUnmigratedDestinations(array('revision_uid', 'status', 'promote',
  761. 'revision', 'log', 'language', 'tnid', 'is_new', 'body:format',
  762. 'body:language', 'migrate_example_wine_regions:create_term', 'comment',
  763. 'migrate_example_wine_varieties:create_term', 'migrate_example_wine_best_with:create_term',
  764. 'field_migrate_example_image:language', 'field_migrate_example_image:preserve_files',
  765. 'field_migrate_example_image:source_dir', 'field_migrate_example_image:destination_dir',
  766. 'field_migrate_example_image:destination_file', 'field_migrate_example_image:file_class',
  767. ));
  768. if (module_exists('statistics')) {
  769. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  770. }
  771. }
  772. protected function addTitlePrefix($source_title) {
  773. return t('review: !title', array('!title' => $source_title));
  774. }
  775. // TIP: Implement a prepareRow() method to manipulate the source row between
  776. // retrieval from the database and the automatic applicaton of mappings
  777. public function prepareRow($current_row) {
  778. // We used the MySQL GROUP_CONCAT function above to handle a multi-value source
  779. // field - more portably, we query the related table with multiple values here,
  780. // so the values can run through the mapping process
  781. $source_id = $current_row->wineid;
  782. $result = db_select('migrate_example_wine_vintages', 'v')
  783. ->fields('v', array('vintage'))
  784. ->condition('wineid', $source_id)
  785. ->execute();
  786. foreach ($result as $row) {
  787. $current_row->best_vintages[] = $row->vintage;
  788. }
  789. // We can have multiple files per node, so we pull them here along with
  790. // their related data (alt/title).
  791. /*
  792. * This is disabled - see http://drupal.org/node/1679798. To demonstrate
  793. * remote file migration, edit the migrate_example_wine_files table and enter
  794. * the URLs of known remote image files, then enable this code.
  795. $result = db_select('migrate_example_wine_files', 'f')
  796. ->fields('f', array('url', 'image_alt', 'image_title'))
  797. ->condition('wineid', $source_id)
  798. ->execute();
  799. foreach ($result as $row) {
  800. $current_row->url[] = $row->url;
  801. $current_row->image_alt[] = $row->image_alt;
  802. $current_row->image_title[] = $row->image_title;
  803. }
  804. */
  805. // We could also have used this function to decide to skip a row, in cases
  806. // where that couldn't easily be done through the original query. Simply
  807. // return FALSE in such cases.
  808. return TRUE;
  809. }
  810. }
  811. class WineCommentMigration extends AdvancedExampleMigration {
  812. public function __construct() {
  813. parent::__construct();
  814. $this->description = 'Comments about wines';
  815. $this->dependencies = array('WineUser', 'WineWine');
  816. $this->map = new MigrateSQLMap($this->machineName,
  817. array('commentid' => array(
  818. 'type' => 'int',
  819. 'unsigned' => TRUE,
  820. 'not null' => TRUE,
  821. )
  822. ),
  823. MigrateDestinationComment::getKeySchema()
  824. );
  825. $query = db_select('migrate_example_wine_comment', 'wc')
  826. ->fields('wc', array('commentid', 'comment_parent', 'name', 'mail',
  827. 'accountid', 'body', 'wineid', 'subject', 'commenthost', 'userpage',
  828. 'posted', 'lastchanged'))
  829. ->orderBy('comment_parent');
  830. $this->source = new MigrateSourceSQL($query);
  831. $this->destination = new MigrateDestinationComment('comment_node_migrate_example_wine');
  832. // Mapped fields
  833. $this->addSimpleMappings(array('name', 'subject', 'mail'));
  834. $this->addFieldMapping('status')
  835. ->defaultValue(COMMENT_PUBLISHED);
  836. $this->addFieldMapping('nid', 'wineid')
  837. ->sourceMigration('WineWine');
  838. $this->addFieldMapping('uid', 'accountid')
  839. ->sourceMigration('WineUser')
  840. ->defaultValue(0);
  841. $this->addFieldMapping('pid', 'comment_parent')
  842. ->sourceMigration('WineComment')
  843. ->description('Parent comment');
  844. $this->addFieldMapping('comment_body', 'body');
  845. $this->addFieldMapping('hostname', 'commenthost');
  846. $this->addFieldMapping('created', 'posted');
  847. $this->addFieldMapping('changed', 'lastchanged');
  848. $this->addFieldMapping('homepage', 'userpage');
  849. // No unmapped source fields
  850. // Unmapped destination fields
  851. $this->addUnmigratedDestinations(array('thread', 'language',
  852. 'comment_body:format', 'comment_body:language'));
  853. $this->removeFieldMapping('path');
  854. $this->removeFieldMapping('pathauto');
  855. }
  856. }
  857. // TIP: An easy way to simply migrate into a Drupal table (i.e., one defined
  858. // through the Schema API) is to use the MigrateDestinationTable destination.
  859. // Just pass the table name to getKeySchema and the MigrateDestinationTable constructor.
  860. class WineTableMigration extends AdvancedExampleMigration {
  861. public function __construct() {
  862. parent::__construct();
  863. $this->description = 'Miscellaneous table data';
  864. $this->softDependencies = array('WineComment');
  865. $table_name = 'migrate_example_wine_table_dest';
  866. $this->map = new MigrateSQLMap($this->machineName,
  867. array('fooid' => array(
  868. 'type' => 'int',
  869. 'unsigned' => TRUE,
  870. 'not null' => TRUE,
  871. )
  872. ),
  873. MigrateDestinationTable::getKeySchema($table_name)
  874. );
  875. $query = db_select('migrate_example_wine_table_source', 't')
  876. ->fields('t', array('fooid', 'field1', 'field2'));
  877. $this->source = new MigrateSourceSQL($query);
  878. $this->destination = new MigrateDestinationTable($table_name);
  879. // Mapped fields
  880. $this->addFieldMapping('drupal_text', 'field1');
  881. $this->addFieldMapping('drupal_int', 'field2');
  882. $this->addUnmigratedDestinations(array('recordid'));
  883. $this->removeFieldMapping('path');
  884. $this->removeFieldMapping('pathauto');
  885. }
  886. }
  887. /**
  888. * This migration works with WinePrepMigration to make ensure auto_nodetitle
  889. * is re-enabled if we disabled it.
  890. */
  891. class WineFinishMigration extends MigrationBase {
  892. public function __construct() {
  893. parent::__construct(MigrateGroup::getInstance('wine', array('default')));
  894. $this->description = t('If auto_nodetitle is present and was previously enabled,
  895. re-enable it');
  896. $this->dependencies = array('WineComment');
  897. }
  898. public function isComplete() {
  899. if (module_exists('auto_nodetitle')) {
  900. return TRUE;
  901. }
  902. else {
  903. return FALSE;
  904. }
  905. }
  906. public function import() {
  907. if (!module_exists('auto_nodetitle')) {
  908. if (WinePrepMigration::$wasEnabled) {
  909. module_enable(array('auto_nodetitle'));
  910. self::displayMessage(t('Re-enabled auto_nodetitle module'), 'success');
  911. }
  912. else {
  913. self::displayMessage(t('auto_nodetitle was not originally enabled'), 'success');
  914. }
  915. }
  916. else {
  917. self::displayMessage(t('Auto_nodetitle module already enabled'), 'success');
  918. }
  919. return Migration::RESULT_COMPLETED;
  920. }
  921. }
  922. /**
  923. * TIP: This demonstrates a migration designed not to import new content, but
  924. * to update existing content (in this case, revised wine ratings)
  925. */
  926. class WineUpdatesMigration extends AdvancedExampleMigration {
  927. public function __construct() {
  928. parent::__construct();
  929. $this->description = t('Update wine ratings');
  930. $this->dependencies = array('WineWine');
  931. $this->softDependencies = array('WineFinish');
  932. $this->map = new MigrateSQLMap($this->machineName,
  933. array(
  934. 'wineid' => array(
  935. 'type' => 'int',
  936. 'unsigned' => TRUE,
  937. 'not null' => TRUE,
  938. 'description' => 'Wine ID',
  939. 'alias' => 'w',
  940. )
  941. ),
  942. MigrateDestinationNode::getKeySchema()
  943. );
  944. $query = db_select('migrate_example_wine_updates', 'w')
  945. ->fields('w', array('wineid', 'rating'));
  946. $this->source = new MigrateSourceSQL($query);
  947. $this->destination = new MigrateDestinationNode('migrate_example_wine');
  948. // Indicate we're updating existing data. The default, Migration::SOURCE, would
  949. // cause existing nodes to be completely replaced by the source data. In this
  950. // case, the existing node will be loaded and only the rating altered.
  951. $this->systemOfRecord = Migration::DESTINATION;
  952. // Mapped fields
  953. // The destination handler needs the nid to change - since the incoming data
  954. // has a source id, not a nid, we need to apply the original wine migration
  955. // mapping to populate the nid.
  956. $this->addFieldMapping('nid', 'wineid')
  957. ->sourceMigration('WineWine');
  958. $this->addFieldMapping('field_migrate_example_wine_ratin', 'rating');
  959. // No unmapped source fields
  960. // Unmapped destination fields
  961. $this->addFieldMapping('uid');
  962. $this->addFieldMapping('migrate_example_wine_varieties');
  963. $this->addFieldMapping('migrate_example_wine_regions');
  964. $this->addFieldMapping('migrate_example_wine_best_with');
  965. $this->addFieldMapping('body');
  966. $this->addFieldMapping('field_migrate_example_image');
  967. $this->addFieldMapping('sticky');
  968. $this->addFieldMapping('created');
  969. $this->addFieldMapping('changed');
  970. $this->addUnmigratedDestinations(array('title', 'revision_uid', 'status', 'promote',
  971. 'revision', 'log', 'language', 'tnid', 'is_new', 'body:format', 'body:summary',
  972. 'body:language', 'migrate_example_wine_regions:source_type',
  973. 'migrate_example_wine_regions:create_term', 'comment',
  974. 'migrate_example_wine_varieties:source_type', 'migrate_example_wine_varieties:create_term',
  975. 'migrate_example_wine_best_with:source_type', 'migrate_example_wine_best_with:create_term',
  976. 'field_migrate_example_image:language', 'field_migrate_example_image:destination_dir',
  977. 'field_migrate_example_image:alt', 'field_migrate_example_image:title',
  978. 'field_migrate_example_image:file_class', 'field_migrate_example_image:file_replace',
  979. 'field_migrate_example_image:preserve_files', 'field_migrate_example_image:destination_file',
  980. 'field_migrate_example_image:source_dir', 'field_migrate_example_top_vintag'));
  981. if (module_exists('statistics')) {
  982. $this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
  983. }
  984. }
  985. }
  986. class WineCommentUpdatesMigration extends AdvancedExampleMigration {
  987. public function __construct() {
  988. parent::__construct();
  989. $this->description = 'Update wine comments';
  990. $this->dependencies = array('WineComment');
  991. $this->softDependencies = array('WineUpdates');
  992. $this->map = new MigrateSQLMap($this->machineName,
  993. array('commentid' => array(
  994. 'type' => 'int',
  995. 'unsigned' => TRUE,
  996. 'not null' => TRUE,
  997. )
  998. ),
  999. MigrateDestinationComment::getKeySchema()
  1000. );
  1001. $query = db_select('migrate_example_wine_comment_updates', 'wc')
  1002. ->fields('wc', array('commentid', 'subject'));
  1003. $this->source = new MigrateSourceSQL($query);
  1004. $this->destination = new MigrateDestinationComment('comment_node_migrate_example_wine');
  1005. $this->systemOfRecord = Migration::DESTINATION;
  1006. // Mapped fields
  1007. $this->addFieldMapping('cid', 'commentid')
  1008. ->sourceMigration('WineComment');
  1009. $this->addFieldMapping('subject', 'subject');
  1010. // No unmapped source fields
  1011. // Unmapped destination fields
  1012. $this->addFieldMapping('name');
  1013. $this->addFieldMapping('mail');
  1014. $this->addFieldMapping('status');
  1015. $this->addFieldMapping('nid');
  1016. $this->addFieldMapping('uid');
  1017. $this->addFieldMapping('pid');
  1018. $this->addFieldMapping('comment_body');
  1019. $this->addFieldMapping('hostname');
  1020. $this->addFieldMapping('created');
  1021. $this->addFieldMapping('changed');
  1022. $this->addFieldMapping('homepage');
  1023. $this->addUnmigratedDestinations(array('thread', 'language',
  1024. 'comment_body:format', 'comment_body:language'));
  1025. $this->removeFieldMapping('path');
  1026. $this->removeFieldMapping('pathauto');
  1027. }
  1028. }
  1029. class WineVarietyUpdatesMigration extends AdvancedExampleMigration {
  1030. public function __construct() {
  1031. parent::__construct();
  1032. $this->description = t('Migrate varieties from the source database to taxonomy terms');
  1033. $this->dependencies = array('WineVariety');
  1034. $this->softDependencies = array('WineUpdates');
  1035. $this->map = new MigrateSQLMap($this->machineName,
  1036. array(
  1037. 'categoryid' => array('type' => 'int',
  1038. 'unsigned' => TRUE,
  1039. 'not null' => TRUE,
  1040. )
  1041. ),
  1042. MigrateDestinationTerm::getKeySchema()
  1043. );
  1044. $query = db_select('migrate_example_wine_variety_updates', 'wc')
  1045. ->fields('wc', array('categoryid', 'details'));
  1046. $this->source = new MigrateSourceSQL($query);
  1047. $this->destination = new MigrateDestinationTerm('migrate_example_wine_varieties');
  1048. $this->systemOfRecord = Migration::DESTINATION;
  1049. // Mapped fields
  1050. $this->addFieldMapping('tid', 'categoryid')
  1051. ->sourceMigration('WineVariety');
  1052. $this->addFieldMapping('description', 'details');
  1053. // Unmapped source fields
  1054. // Unmapped destination fields
  1055. $this->addFieldMapping('name');
  1056. $this->addFieldMapping('parent');
  1057. $this->addFieldMapping('weight');
  1058. $this->addFieldMapping('format');
  1059. $this->addFieldMapping('parent_name')
  1060. ->issueGroup(t('DNM'));
  1061. }
  1062. }
  1063. class WineUserUpdatesMigration extends AdvancedExampleMigration {
  1064. public function __construct() {
  1065. parent::__construct();
  1066. $this->description = t('Account updates');
  1067. $this->dependencies = array('WineUser');
  1068. $this->softDependencies = array('WineUpdates');
  1069. $this->map = new MigrateSQLMap($this->machineName,
  1070. array('accountid' => array(
  1071. 'type' => 'int',
  1072. 'unsigned' => TRUE,
  1073. 'not null' => TRUE,
  1074. 'description' => 'Account ID.'
  1075. )
  1076. ),
  1077. MigrateDestinationUser::getKeySchema()
  1078. );
  1079. $query = db_select('migrate_example_wine_account_updates', 'wa')
  1080. ->fields('wa', array('accountid', 'sex'));
  1081. $this->source = new MigrateSourceSQL($query);
  1082. $this->destination = new MigrateDestinationUser();
  1083. $this->systemOfRecord = Migration::DESTINATION;
  1084. // Mapped fields
  1085. $this->addFieldMapping('uid', 'accountid')
  1086. ->sourceMigration('WineUser');
  1087. $this->addFieldMapping('field_migrate_example_gender', 'sex')
  1088. ->description(t('Map from M/F to 0/1 in prepare method'));
  1089. // Unmapped source fields
  1090. // Unmapped destination fields
  1091. $this->addFieldMapping('name');
  1092. $this->addFieldMapping('status');
  1093. $this->addFieldMapping('created');
  1094. $this->addFieldMapping('access');
  1095. $this->addFieldMapping('login');
  1096. $this->addFieldMapping('mail');
  1097. $this->addFieldMapping('pass');
  1098. $this->addFieldMapping('roles');
  1099. $this->addFieldMapping('signature');
  1100. $this->addFieldMapping('signature_format');
  1101. $this->addFieldMapping('init');
  1102. $this->addUnmigratedDestinations(array('theme', 'timezone', 'language',
  1103. 'picture', 'is_new', 'field_migrate_example_favbeers'));
  1104. $this->removeFieldMapping('path');
  1105. }
  1106. public function prepare(stdClass $account, stdClass $row) {
  1107. // Gender data comes in as M/F, needs to be saved as Male=0/Female=1
  1108. // TIP: Note that the Migration prepare method is called after all other
  1109. // prepare handlers. Most notably, the field handlers have had their way
  1110. // and created field arrays, so we have to save in the same format.
  1111. switch ($row->sex) {
  1112. case 'm':
  1113. case 'M':
  1114. $account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 0;
  1115. break;
  1116. case 'f':
  1117. case 'F':
  1118. $account->field_migrate_example_gender[LANGUAGE_NONE][0]['value'] = 1;
  1119. break;
  1120. default:
  1121. $account->field_migrate_example_gender = NULL;
  1122. break;
  1123. }
  1124. }
  1125. }