term.inc 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php
  2. /**
  3. * @file
  4. * Support for taxonomy term destinations.
  5. */
  6. // TODO:
  7. // Make sure this works with updates, explicit destination keys
  8. // taxonomy_term_save() is doing a cache_clear_all and an automatic insertion for parent.
  9. /**
  10. * Destination class implementing migration into terms.
  11. */
  12. class MigrateDestinationTerm extends MigrateDestinationEntity {
  13. static public function getKeySchema() {
  14. return array(
  15. 'tid' => array(
  16. 'type' => 'int',
  17. 'unsigned' => TRUE,
  18. 'description' => 'ID of destination term',
  19. ),
  20. );
  21. }
  22. /**
  23. * Return an options array for term destinations.
  24. *
  25. * @param string $language
  26. * Default language for terms created via this destination class.
  27. * @param string $text_format
  28. * Default text format for terms created via this destination class.
  29. */
  30. static public function options($language, $text_format) {
  31. return compact('language', 'text_format');
  32. }
  33. /**
  34. * Basic initialization
  35. *
  36. * @param array $options
  37. * Options applied to terms.
  38. */
  39. public function __construct($bundle, array $options = array()) {
  40. parent::__construct('taxonomy_term', $bundle, $options);
  41. }
  42. /**
  43. * Returns a list of fields available to be mapped for this vocabulary (bundle)
  44. *
  45. * @param Migration $migration
  46. * Optionally, the migration containing this destination.
  47. * @return array
  48. * Keys: machine names of the fields (to be passed to addFieldMapping)
  49. * Values: Human-friendly descriptions of the fields.
  50. */
  51. public function fields($migration = NULL) {
  52. $fields = array();
  53. // First the core (taxonomy_term_data table) properties
  54. $fields['tid'] = t('Term: <a href="@doc">Existing term ID</a>',
  55. array('@doc' => 'http://drupal.org/node/1349702#tid'));
  56. $fields['name'] = t('Term: <a href="@doc">Name</a>',
  57. array('@doc' => 'http://drupal.org/node/1349702#name'));
  58. $fields['description'] = t('Term: <a href="@doc">Description</a>',
  59. array('@doc' => 'http://drupal.org/node/1349702#description'));
  60. $fields['parent'] = t('Term: <a href="@doc">Parent (by Drupal term ID)</a>',
  61. array('@doc' => 'http://drupal.org/node/1349702#parent'));
  62. // TODO: Remove parent_name, implement via arguments
  63. $fields['parent_name'] = t('Term: <a href="@doc">Parent (by name)</a>',
  64. array('@doc' => 'http://drupal.org/node/1349702#parent_name'));
  65. $fields['format'] = t('Term: <a href="@doc">Format</a>',
  66. array('@doc' => 'http://drupal.org/node/1349702#format'));
  67. $fields['weight'] = t('Term: <a href="@doc">Weight</a>',
  68. array('@doc' => 'http://drupal.org/node/1349702#weight'));
  69. // Then add in anything provided by handlers
  70. $fields += migrate_handler_invoke_all('entity', 'fields', $this->entityType, $this->bundle, $migration);
  71. $fields += migrate_handler_invoke_all('taxonomy_term', 'fields', $this->entityType, $this->bundle, $migration);
  72. return $fields;
  73. }
  74. /**
  75. * Delete a migrated term
  76. *
  77. * @param $ids
  78. * Array of fields representing the key (in this case, just tid).
  79. */
  80. public function rollback(array $key) {
  81. $tid = reset($key);
  82. /*
  83. * This load() happens soon delete() anyway. We load here in order to
  84. * avoid notices when term has already been deleted. That is easily possible
  85. * considering how deleting a term parent also deletes children in same call.
  86. */
  87. migrate_instrument_start('taxonomy_term_load');
  88. if (taxonomy_term_load($tid)) {
  89. migrate_instrument_stop('taxonomy_term_load');
  90. migrate_instrument_start('taxonomy_term_delete');
  91. $this->prepareRollback($tid);
  92. $result = (bool) taxonomy_term_delete($tid);
  93. $this->completeRollback($tid);
  94. migrate_instrument_stop('taxonomy_term_delete');
  95. }
  96. else {
  97. migrate_instrument_stop('taxonomy_term_load');
  98. // If it didn't exist, consider this a success
  99. $result = TRUE;
  100. }
  101. return $result;
  102. }
  103. /**
  104. * Import a single term.
  105. *
  106. * @param $term
  107. * Term object to build. Prefilled with any fields mapped in the Migration.
  108. * @param $row
  109. * Raw source data object - passed through to prepare/complete handlers.
  110. * @return array
  111. * Array of key fields (tid only in this case) of the term that was saved if
  112. * successful. FALSE on failure.
  113. */
  114. public function import(stdClass $term, stdClass $row) {
  115. $migration = Migration::currentMigration();
  116. // Updating previously-migrated content?
  117. if (isset($row->migrate_map_destid1)) {
  118. $term->tid = $row->migrate_map_destid1;
  119. if (isset($term->tid)) {
  120. if ($term->tid != $row->migrate_map_destid1) {
  121. throw new MigrateException(t("Incoming tid !tid and map destination nid !destid1 don't match",
  122. array('!tid' => $term->tid, '!destid1' => $row->migrate_map_destid1)));
  123. }
  124. }
  125. else {
  126. $term->tid = $row->migrate_map_destid1;
  127. }
  128. }
  129. if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
  130. if (!isset($term->tid)) {
  131. throw new MigrateException(t('System-of-record is DESTINATION, but no destination tid provided'));
  132. }
  133. $rawterm = $term;
  134. $this->prepare($term, $row);
  135. $old_term = taxonomy_term_load($term->tid);
  136. if (empty($old_term)) {
  137. throw new MigrateException(t('System-of-record is DESTINATION, but term !tid does not exist',
  138. array('!tid' => $term->tid)));
  139. }
  140. foreach ($rawterm as $field => $value) {
  141. $old_term->$field = $term->$field;
  142. }
  143. $term = $old_term;
  144. }
  145. else {
  146. // Default to bundle if no vocabulary machine name provided
  147. if (!isset($term->vocabulary_machine_name)) {
  148. $term->vocabulary_machine_name = $this->bundle;
  149. }
  150. // vid is required
  151. if (empty($term->vid)) {
  152. static $vocab_map = array();
  153. if (!isset($vocab_map[$term->vocabulary_machine_name])) {
  154. // The keys of the returned array are vids
  155. $vocabs = taxonomy_vocabulary_load_multiple(array(),
  156. array('machine_name' => $term->vocabulary_machine_name));
  157. $vids = array_keys($vocabs);
  158. if (isset($vids[0])) {
  159. $vocab_map[$term->vocabulary_machine_name] = $vids[0];
  160. }
  161. else {
  162. $migration->saveMessage(t('No vocabulary found with machine_name !name',
  163. array('!name' => $term->vocabulary_machine_name)));
  164. return FALSE;
  165. }
  166. }
  167. $term->vid = $vocab_map[$term->vocabulary_machine_name];
  168. }
  169. // Look up parent name if provided
  170. if (isset($term->parent_name) && trim($term->parent_name)) {
  171. // Look for the name in the same vocabulary.
  172. // Note that hierarchies may have multiples of the same name...
  173. $terms = taxonomy_term_load_multiple(array(),
  174. array('name' => trim($term->parent_name), 'vid' => $term->vid));
  175. $tids = array_keys($terms);
  176. $term->parent = array($tids[0]);
  177. unset($term->parent_name);
  178. }
  179. if (empty($term->parent)) {
  180. $term->parent = array(0);
  181. }
  182. if (is_array($term->parent) && isset($term->parent['arguments'])) {
  183. // Unset arguments here to avoid duplicate entries in the
  184. // term_hierarchy table.
  185. unset($term->parent['arguments']);
  186. }
  187. if (!isset($term->format)) {
  188. $term->format = $this->textFormat;
  189. }
  190. $this->prepare($term, $row);
  191. // See if the term, with the same parentage, already exists - if so,
  192. // load it
  193. $candidates = taxonomy_term_load_multiple(array(),
  194. array('name' => trim($term->name), 'vid' => $term->vid));
  195. foreach ($candidates as $candidate) {
  196. $parents = taxonomy_get_parents($candidate->tid);
  197. // We need to set up $parents as a simple array of tids
  198. if (empty($parents)) {
  199. $parents = array(0);
  200. }
  201. else {
  202. // Parents array is tid => term object, make into list of tids
  203. $new_parents = array();
  204. foreach ($parents as $parent) {
  205. $new_parents[] = $parent->tid;
  206. }
  207. $parents = $new_parents;
  208. }
  209. if ($term->parent == $parents) {
  210. // We've found a matching term, we'll use that
  211. $term = $candidate;
  212. break;
  213. }
  214. }
  215. }
  216. // Trying to update an existing term
  217. if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
  218. $existing_term = taxonomy_term_load($term->tid);
  219. if ($existing_term) {
  220. // Incoming data overrides existing data, so only copy non-existent fields
  221. foreach ($existing_term as $field => $value) {
  222. if (!isset($term->$field)) {
  223. $term->$field = $existing_term->$field;
  224. }
  225. }
  226. }
  227. }
  228. if (isset($term->tid)) {
  229. $updating = TRUE;
  230. }
  231. else {
  232. $updating = FALSE;
  233. }
  234. migrate_instrument_start('taxonomy_term_save');
  235. $status = taxonomy_term_save($term);
  236. migrate_instrument_stop('taxonomy_term_save');
  237. $this->complete($term, $row);
  238. if (isset($term->tid)) {
  239. if ($updating) {
  240. $this->numUpdated++;
  241. }
  242. else {
  243. $this->numCreated++;
  244. }
  245. $return = array($term->tid);
  246. }
  247. else {
  248. $return = FALSE;
  249. }
  250. return $return;
  251. }
  252. }