taxonomy.install 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * @file
  4. * Install, update and uninstall functions for the taxonomy module.
  5. */
  6. use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
  7. use Drupal\Core\Field\BaseFieldDefinition;
  8. use Drupal\Core\Site\Settings;
  9. /**
  10. * Implements hook_requirements().
  11. */
  12. function taxonomy_requirements($phase) {
  13. $requirements = [];
  14. if ($phase === 'update') {
  15. // Check for invalid data before making terms revisionable.
  16. /** @var \Drupal\Core\Update\UpdateRegistry $registry */
  17. $registry = \Drupal::service('update.post_update_registry');
  18. $update_name = 'taxonomy_post_update_make_taxonomy_term_revisionable';
  19. if (in_array($update_name, $registry->getPendingUpdateFunctions(), TRUE)) {
  20. // The 'name' field is non-NULL - if we get a NULL value that indicates a
  21. // failure to join on taxonomy_term_field_data.
  22. $is_broken = \Drupal::entityQuery('taxonomy_term')
  23. ->condition('name', NULL, 'IS NULL')
  24. ->range(0, 1)
  25. ->accessCheck(FALSE)
  26. ->execute();
  27. if ($is_broken) {
  28. $requirements[$update_name] = [
  29. 'title' => t('Taxonomy term data'),
  30. 'value' => t('Integrity issues detected'),
  31. 'description' => t('The make_taxonomy_term_revisionable database update cannot be run until the data has been fixed. See the <a href=":change_record">change record</a> for more information.', [
  32. ':change_record' => 'https://www.drupal.org/node/3117753',
  33. ]),
  34. 'severity' => REQUIREMENT_ERROR,
  35. ];
  36. }
  37. }
  38. }
  39. return $requirements;
  40. }
  41. /**
  42. * Convert the custom taxonomy term hierarchy storage to a default storage.
  43. */
  44. function taxonomy_update_8501() {
  45. $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  46. /** @var \Drupal\Core\Field\BaseFieldDefinition $field_storage_definition */
  47. $field_storage_definition = $definition_update_manager->getFieldStorageDefinition('parent', 'taxonomy_term');
  48. $field_storage_definition->setCustomStorage(FALSE);
  49. $definition_update_manager->updateFieldStorageDefinition($field_storage_definition);
  50. }
  51. /**
  52. * Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}.
  53. */
  54. function taxonomy_update_8502(&$sandbox) {
  55. $database = \Drupal::database();
  56. if (!isset($sandbox['current'])) {
  57. // Set batch ops sandbox.
  58. $sandbox['current'] = 0;
  59. $sandbox['tid'] = -1;
  60. $sandbox['delta'] = 0;
  61. $sandbox['limit'] = Settings::get('entity_update_batch_size', 50);
  62. // Count records using a join, as there might be orphans in the hierarchy
  63. // table. See https://www.drupal.org/project/drupal/issues/2997982.
  64. $select = $database->select('taxonomy_term_hierarchy', 'h');
  65. $select->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
  66. $sandbox['max'] = $select
  67. ->countQuery()
  68. ->execute()
  69. ->fetchField();
  70. }
  71. // Save the hierarchy.
  72. $select = $database->select('taxonomy_term_hierarchy', 'h');
  73. $select->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
  74. $hierarchy = $select
  75. ->fields('h', ['tid', 'parent'])
  76. ->fields('d', ['vid', 'langcode'])
  77. ->range($sandbox['current'], $sandbox['limit'])
  78. ->orderBy('tid', 'ASC')
  79. ->orderBy('parent', 'ASC')
  80. ->execute()
  81. ->fetchAll();
  82. // Restore data.
  83. $insert = $database->insert('taxonomy_term__parent')
  84. ->fields(['bundle', 'entity_id', 'revision_id', 'langcode', 'delta', 'parent_target_id']);
  85. foreach ($hierarchy as $row) {
  86. if ($row->tid !== $sandbox['tid']) {
  87. $sandbox['delta'] = 0;
  88. $sandbox['tid'] = $row->tid;
  89. }
  90. $insert->values([
  91. 'bundle' => $row->vid,
  92. 'entity_id' => $row->tid,
  93. 'revision_id' => $row->tid,
  94. 'langcode' => $row->langcode,
  95. 'delta' => $sandbox['delta'],
  96. 'parent_target_id' => $row->parent,
  97. ]);
  98. $sandbox['delta']++;
  99. $sandbox['current']++;
  100. }
  101. $insert->execute();
  102. $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);
  103. if ($sandbox['#finished'] >= 1) {
  104. // Update the entity type because the 'taxonomy_term_hierarchy' table is no
  105. // longer part of its shared tables schema.
  106. $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  107. $definition_update_manager->updateEntityType($definition_update_manager->getEntityType('taxonomy_term'));
  108. // \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate()
  109. // only deletes *known* entity tables (i.e. the base, data and revision
  110. // tables), so we have to drop it manually.
  111. $database->schema()->dropTable('taxonomy_term_hierarchy');
  112. return t('Taxonomy term hierarchy has been converted to default entity reference storage.');
  113. }
  114. }
  115. /**
  116. * Update views to use {taxonomy_term__parent} in relationships.
  117. */
  118. function taxonomy_update_8503() {
  119. $config_factory = \Drupal::configFactory();
  120. foreach ($config_factory->listAll('views.view.') as $id) {
  121. $view = $config_factory->getEditable($id);
  122. foreach (array_keys($view->get('display')) as $display_id) {
  123. $changed = FALSE;
  124. foreach (['relationships', 'filters', 'arguments'] as $handler_type) {
  125. $base_path = "display.$display_id.display_options.$handler_type";
  126. $handlers = $view->get($base_path);
  127. if (!$handlers) {
  128. continue;
  129. }
  130. foreach ($handlers as $handler_key => $handler_config) {
  131. $table_path = "$base_path.$handler_key.table";
  132. $field_path = "$base_path.$handler_key.field";
  133. $table = $view->get($table_path);
  134. $field = $view->get($field_path);
  135. if (($table && ($table === 'taxonomy_term_hierarchy')) && ($field && ($field === 'parent'))) {
  136. $view->set($table_path, 'taxonomy_term__parent');
  137. $view->set($field_path, 'parent_target_id');
  138. $changed = TRUE;
  139. }
  140. }
  141. }
  142. if ($changed) {
  143. $view->save(TRUE);
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * Add the publishing status fields to taxonomy terms.
  150. */
  151. function taxonomy_update_8601() {
  152. $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  153. $entity_type = $definition_update_manager->getEntityType('taxonomy_term');
  154. // Bail out early if a field named 'status' is already installed.
  155. if ($definition_update_manager->getFieldStorageDefinition('status', 'taxonomy_term')) {
  156. $message = \Drupal::state()->get('taxonomy_update_8601_skip_message', t('The publishing status field has <strong>not</strong> been added to taxonomy terms. See <a href=":link">this page</a> for more information on how to install it.', [
  157. ':link' => 'https://www.drupal.org/node/2985366',
  158. ]));
  159. return $message;
  160. }
  161. // Add the 'published' entity key to the taxonomy_term entity type.
  162. $entity_keys = $entity_type->getKeys();
  163. $entity_keys['published'] = 'status';
  164. $entity_type->set('entity_keys', $entity_keys);
  165. $definition_update_manager->updateEntityType($entity_type);
  166. // Add the status field.
  167. $status = BaseFieldDefinition::create('boolean')
  168. ->setLabel(t('Publishing status'))
  169. ->setDescription(t('A boolean indicating the published state.'))
  170. ->setRevisionable(TRUE)
  171. ->setTranslatable(TRUE)
  172. ->setDefaultValue(TRUE);
  173. $has_content_translation_status_field = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
  174. if ($has_content_translation_status_field) {
  175. $status->setInitialValueFromField('content_translation_status', TRUE);
  176. }
  177. else {
  178. $status->setInitialValue(TRUE);
  179. }
  180. $definition_update_manager->installFieldStorageDefinition('status', 'taxonomy_term', 'taxonomy_term', $status);
  181. // Uninstall the 'content_translation_status' field if needed.
  182. if ($has_content_translation_status_field) {
  183. $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
  184. $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
  185. }
  186. return t('The publishing status field has been added to taxonomy terms.');
  187. }
  188. /**
  189. * Add an index on the 'taxonomy_term__parent' field table.
  190. */
  191. function taxonomy_update_8701() {
  192. $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  193. $storage_definition = $entity_definition_update_manager->getFieldStorageDefinition('parent', 'taxonomy_term');
  194. $entity_definition_update_manager->updateFieldStorageDefinition($storage_definition);
  195. }
  196. /**
  197. * Fix the parent field langcode data.
  198. */
  199. function taxonomy_update_8702(&$sandbox) {
  200. $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  201. $field_storage_definition = $definition_update_manager->getFieldStorageDefinition('parent', 'taxonomy_term');
  202. $entity_type = $definition_update_manager->getEntityType('taxonomy_term');
  203. // Only perform the update if:
  204. // - The field is not translatable. It's possible that custom or contrib code
  205. // has overridden this.
  206. // - The field is not revisionable. If it is then
  207. // taxonomy_post_update_make_taxonomy_term_revisionable() has already run
  208. // and this used to fix the parent field langcode data.
  209. // - Terms are using a SQL-based storage class.
  210. if (!$field_storage_definition->isTranslatable() &&
  211. !$entity_type->isRevisionable() &&
  212. is_subclass_of($entity_type->getStorageClass(), SqlEntityStorageInterface::class)
  213. ) {
  214. // taxonomy_update_8502() populated the langcode field of
  215. // 'taxonomy_term__parent' using the term's langcode. However, the field is
  216. // not translatable and, therefore, should use the term's default language.
  217. $database = \Drupal::database();
  218. $select = $database->select('taxonomy_term__parent', 'tp');
  219. $select->join('taxonomy_term_field_data', 'tdf', 'tp.entity_id = tdf.tid AND tdf.langcode <> tp.langcode');
  220. $select->fields('tp', ['entity_id'])
  221. ->fields('tdf', ['tid', 'langcode'])
  222. ->condition('tdf.default_langcode', 1);
  223. if (!isset($sandbox['max'])) {
  224. $count_query = clone $select;
  225. $sandbox['max'] = $count_query->countQuery()->execute()->fetchField();
  226. $sandbox['current'] = 0;
  227. }
  228. $result = $select->execute();
  229. $processed = 0;
  230. while ($row = $result->fetchAssoc()) {
  231. $database->update('taxonomy_term__parent')
  232. ->condition('entity_id', $row['tid'])
  233. ->fields(['langcode' => $row['langcode']])
  234. ->execute();
  235. $sandbox['current']++;
  236. $processed++;
  237. if ($processed >= Settings::get('entity_update_batch_size', 50)) {
  238. break;
  239. }
  240. }
  241. }
  242. $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);
  243. }