synonyms_views_plugin_argument_validate_taxonomy_term.inc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <?php
  2. /**
  3. * @file
  4. * An extended version of the 'taxonomy term' argument validator plugin, which
  5. * supports synonyms as arguments.
  6. */
  7. /**
  8. * Validate whether an argument is an acceptable taxonomy term.
  9. */
  10. class synonyms_views_plugin_argument_validate_taxonomy_term extends views_plugin_argument_validate_taxonomy_term {
  11. function options_form(&$form, &$form_state) {
  12. parent::options_form($form, $form_state);
  13. // We just want to add yet another way of validation - synonyms friendly
  14. // validation.
  15. $form['type']['#options']['synonym'] = t('Term name or synonym');
  16. $form['type']['#options']['synonym_tid'] = t('Term name or synonym converted to Term ID');
  17. }
  18. function validate_argument($argument) {
  19. $vocabularies = array_filter($this->options['vocabularies']);
  20. $type = $this->options['type'];
  21. $transform = $this->options['transform'];
  22. switch ($type) {
  23. case 'synonym':
  24. case 'synonym_tid':
  25. // We are requested to do synonyms friendly validation. We will have to
  26. // go through each of allowed vocabularies, collecting terms whose
  27. // synonyms might look like the argument we have. However, it is not
  28. // that easy to achieve due to the fact that synonyms may be kept in
  29. // whatever format in whatever column and only corresponding Synonyms
  30. // extractor class has the knowledge how it is organized. Firstly we are
  31. // going to query database trying to find a term with our argument's
  32. // name. If we find one, it is great and we stop right there. Otherwise,
  33. // the nightmare starts: we are going to use
  34. // AbstractSynonymsExtractor::processEntityFieldQuery() method to find
  35. // synonyms that are similar to our argument. Then we will load those
  36. // terms and manually in PHP will determine whether it is a match.
  37. $query = db_select('taxonomy_term_data', 't')
  38. ->fields('t', array('tid', 'name'));
  39. if (!empty($vocabularies)) {
  40. $query->innerJoin('taxonomy_vocabulary', 'v', 't.vid = v.vid');
  41. $query->condition('v.machine_name', $vocabularies);
  42. }
  43. if ($transform) {
  44. $query->where("replace(t.name, ' ', '-') = :name", array(':name' => $argument));
  45. }
  46. else {
  47. $query->condition('t.name', $argument, '=');
  48. }
  49. $result = $query->range(0, 1)
  50. ->execute();
  51. if ($result->rowCount()) {
  52. // We have found a term, we are going to use it.
  53. $term = taxonomy_term_load($result->fetchField(0));
  54. $this->argument->argument = $this->synonyms_get_term_property($term);
  55. $this->argument->validated_title = check_plain(entity_label('taxonomy_term', $term));
  56. return TRUE;
  57. }
  58. if (empty($vocabularies)) {
  59. // At this point we want to convert an empty $vocabulary (implicitly
  60. // meaning "all") into actually a list of all fully loaded
  61. // vocabularies.
  62. $vocabularies = taxonomy_vocabulary_load_multiple(FALSE);
  63. }
  64. else {
  65. // We need to load the filtered vocabularies based on their machine
  66. // names.
  67. foreach ($vocabularies as $v) {
  68. $vocabularies[$v] = taxonomy_vocabulary_machine_name_load($v);
  69. }
  70. }
  71. // We haven't had much luck, seems like we have to do the hard part. We
  72. // will pull up terms that are similar to our argument using the same
  73. // functionality as for synonyms friendly autocomplete widget and then
  74. // will find those who actually match among this narrowed set of terms.
  75. // Since $match will be use as value for LIKE SQL operator, we are not
  76. // sure whether we need to substitute dash (-) with spacebar, or keep
  77. // the dash, to match both we will use underscore (_) since this symbol
  78. // matches one symbol in LIKE operator.
  79. $match = $transform ? str_replace('-', '_', $argument) : $argument;
  80. $tids = array();
  81. // Unfortunately we have to query multiple times the database for each
  82. // vocabulary for each synonyms-source field.
  83. foreach ($vocabularies as $vocabulary) {
  84. $bundle = field_extract_bundle('taxonomy_term', $vocabulary);
  85. foreach (synonyms_synonyms_fields($vocabulary) as $field) {
  86. $field = field_info_field($field);
  87. $instance = field_info_instance('taxonomy_term', $field['field_name'], $bundle);
  88. $query = new EntityFieldQuery();
  89. $query->entityCondition('entity_type', 'taxonomy_term')
  90. ->entityCondition('bundle', $vocabulary->machine_name);
  91. // We let the class that defines this field type as a source of
  92. // synonyms filter out and provide its suggestions based on this
  93. // field.
  94. $class = synonyms_extractor_info($field['type']);
  95. call_user_func(array($class, 'processEntityFieldQuery'), $match, $query, $field, $instance);
  96. if (!empty($tids)) {
  97. $query->entityCondition('entity_id', $tids, 'NOT IN');
  98. }
  99. $result = $query->execute();
  100. if (isset($result['taxonomy_term'])) {
  101. $tids = array_merge($tids, array_keys($result['taxonomy_term']));
  102. }
  103. }
  104. }
  105. $terms = taxonomy_term_load_multiple($tids);
  106. // We have an array of terms whose synonyms look like they could be
  107. // equal to our argument, let us iterate over each term's synonyms to
  108. // actually see if they match.
  109. $argument = mb_strtoupper($argument, 'UTF-8');
  110. foreach ($terms as $term) {
  111. foreach (synonyms_get_term_synonyms($term) as $synonym) {
  112. $synonym = mb_strtoupper($synonym['safe_value'], 'UTF-8');
  113. if ($transform) {
  114. $synonym = str_replace(' ', '-', $synonym);
  115. }
  116. if ($synonym == $argument) {
  117. // Finally we found a synonym that matches our argument. We are
  118. // going to use the corresponding term and there is no point to
  119. // continue searching.
  120. $this->argument->argument = $this->synonyms_get_term_property($term);
  121. $this->argument->validated_title = check_plain(entity_label('taxonomy_term', $term));
  122. return TRUE;
  123. }
  124. }
  125. }
  126. // We haven't found any synonym that matched our argument, so there is
  127. // no term to return.
  128. return FALSE;
  129. default:
  130. return parent::validate_argument($argument);
  131. }
  132. }
  133. /**
  134. * Return necessary property (per chosen validator) of encountered term.
  135. *
  136. * @param $term object
  137. * Fully loaded taxonomy term from which necessary property should be
  138. * returned
  139. *
  140. * @return mixed
  141. * Necessary property (per chosen validator) of the provided term
  142. */
  143. function synonyms_get_term_property($term) {
  144. switch ($this->options['type']) {
  145. case 'synonym':
  146. return $term->name;
  147. case 'synonym_tid':
  148. return $term->tid;
  149. }
  150. return NULL;
  151. }
  152. }