EntityReferenceBehavior_TaxonomyIndex.class.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. /**
  3. * @file
  4. * CTools plugin class for the taxonomy-index behavior.
  5. */
  6. /**
  7. * Extends an entityreference field to maintain its references to taxonomy terms
  8. * in the {taxonomy_index} table.
  9. *
  10. * Note, unlike entityPostInsert() and entityPostUpdate(), entityDelete()
  11. * is not needed as cleanup is performed by taxonomy module in
  12. * taxonomy_delete_node_index().
  13. */
  14. class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHandler_Abstract {
  15. /**
  16. * Overrides EntityReference_BehaviorHandler_Abstract::access().
  17. *
  18. * Ensure that it is only enabled for ER instances on nodes targeting
  19. * terms, and the core variable to maintain index is enabled.
  20. */
  21. public function access($field, $instance) {
  22. if ($instance['entity_type'] != 'node' || $field['settings']['target_type'] != 'taxonomy_term') {
  23. return;
  24. }
  25. if ($field['storage']['type'] !== 'field_sql_storage') {
  26. // Field doesn't use SQL storage.
  27. return;
  28. }
  29. return variable_get('taxonomy_maintain_index_table', TRUE);
  30. }
  31. /**
  32. * Overrides EntityReference_BehaviorHandler_Abstract::entityPostInsert().
  33. *
  34. * Runs after hook_node_insert() used by taxonomy module.
  35. */
  36. public function entityPostInsert($entity_type, $entity, $field, $instance) {
  37. if ($entity_type != 'node') {
  38. return;
  39. }
  40. $this->buildNodeIndex($entity);
  41. }
  42. /**
  43. * Overrides EntityReference_BehaviorHandler_Abstract::entityPostUpdate().
  44. *
  45. * Runs after hook_node_update() used by taxonomy module.
  46. */
  47. public function entityPostUpdate($entity_type, $entity, $field, $instance) {
  48. if ($entity_type != 'node') {
  49. return;
  50. }
  51. $this->buildNodeIndex($entity);
  52. }
  53. /**
  54. * Builds and inserts taxonomy index entries for a given node.
  55. *
  56. * The index lists all terms that are related to a given node entity, and is
  57. * therefore maintained at the entity level.
  58. *
  59. * @param $node
  60. * The node object.
  61. *
  62. * @see taxonomy_build_node_index()
  63. */
  64. protected function buildNodeIndex($node) {
  65. // We maintain a denormalized table of term/node relationships, containing
  66. // only data for current, published nodes.
  67. $status = NULL;
  68. if (variable_get('taxonomy_maintain_index_table', TRUE)) {
  69. // If a node property is not set in the node object when node_save() is
  70. // called, the old value from $node->original is used.
  71. if (!empty($node->original)) {
  72. $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status)));
  73. $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky)));
  74. }
  75. else {
  76. $status = (int)(!empty($node->status));
  77. $sticky = (int)(!empty($node->sticky));
  78. }
  79. }
  80. // We only maintain the taxonomy index for published nodes.
  81. if ($status) {
  82. // Collect a unique list of all the term IDs from all node fields.
  83. $tid_all = array();
  84. foreach (field_info_instances('node', $node->type) as $instance) {
  85. $field_name = $instance['field_name'];
  86. $field = field_info_field($field_name);
  87. if (!empty($field['settings']['target_type']) && $field['settings']['target_type'] == 'taxonomy_term' && $field['storage']['type'] == 'field_sql_storage') {
  88. // If a field value is not set in the node object when node_save() is
  89. // called, the old value from $node->original is used.
  90. if (isset($node->{$field_name})) {
  91. $items = $node->{$field_name};
  92. }
  93. elseif (isset($node->original->{$field_name})) {
  94. $items = $node->original->{$field_name};
  95. }
  96. else {
  97. continue;
  98. }
  99. foreach (field_available_languages('node', $field) as $langcode) {
  100. if (!empty($items[$langcode])) {
  101. foreach ($items[$langcode] as $item) {
  102. $tid_all[$item['target_id']] = $item['target_id'];
  103. }
  104. }
  105. }
  106. }
  107. // Re-calculate the terms added in taxonomy_build_node_index() so
  108. // we can optimize database queries.
  109. $original_tid_all = array();
  110. if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
  111. // If a field value is not set in the node object when node_save() is
  112. // called, the old value from $node->original is used.
  113. if (isset($node->{$field_name})) {
  114. $items = $node->{$field_name};
  115. }
  116. elseif (isset($node->original->{$field_name})) {
  117. $items = $node->original->{$field_name};
  118. }
  119. else {
  120. continue;
  121. }
  122. foreach (field_available_languages('node', $field) as $langcode) {
  123. if (!empty($items[$langcode])) {
  124. foreach ($items[$langcode] as $item) {
  125. $original_tid_all[$item['tid']] = $item['tid'];
  126. }
  127. }
  128. }
  129. }
  130. }
  131. // Insert index entries for all the node's terms, that were not
  132. // already inserted in taxonomy_build_node_index().
  133. $tid_all = array_diff($tid_all, $original_tid_all);
  134. // Insert index entries for all the node's terms, preventing duplicates.
  135. if (!empty($tid_all)) {
  136. foreach ($tid_all as $tid) {
  137. $row = array(
  138. 'nid' => $node->nid,
  139. 'tid' => $tid,
  140. 'sticky' => $sticky,
  141. 'created' => $node->created,
  142. );
  143. $query = db_merge('taxonomy_index')
  144. ->key($row)
  145. ->fields($row);
  146. $query->execute();
  147. }
  148. }
  149. }
  150. }
  151. /**
  152. * Overrides EntityReference_BehaviorHandler_Abstract::settingsForm().
  153. */
  154. public function settingsForm($field, $instance) {
  155. $form = array();
  156. $target = $field['settings']['target_type'];
  157. if ($target != 'taxonomy_term') {
  158. $form['ti-on-terms'] = array(
  159. '#markup' => t('This behavior can only be set when the target type is taxonomy_term, but the target of this field is %target.', array('%target' => $target)),
  160. );
  161. }
  162. $entity_type = $instance['entity_type'];
  163. if ($entity_type != 'node') {
  164. $form['ti-on-nodes'] = array(
  165. '#markup' => t('This behavior can only be set when the entity type is node, but the entity type of this instance is %type.', array('%type' => $entity_type)),
  166. );
  167. }
  168. if (!variable_get('taxonomy_maintain_index_table', TRUE)) {
  169. $form['ti-disabled'] = array(
  170. '#markup' => t('This core variable "taxonomy_maintain_index_table" is disabled.'),
  171. );
  172. }
  173. return $form;
  174. }
  175. }