ViewsDataHelper.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. namespace Drupal\views;
  3. use Drupal\Component\Utility\Unicode;
  4. use Drupal\Component\Utility\SafeMarkup;
  5. /**
  6. * Defines a helper class for stuff related to views data.
  7. */
  8. class ViewsDataHelper {
  9. /**
  10. * The views data object, containing the cached information.
  11. *
  12. * @var \Drupal\views\ViewsData
  13. */
  14. protected $data;
  15. /**
  16. * A prepared list of all fields, keyed by base_table and handler type.
  17. *
  18. * @var array
  19. */
  20. protected $fields;
  21. /**
  22. * Constructs a ViewsData object.
  23. *
  24. * @param \Drupal\views\ViewsData $views_data
  25. * The views data object, containing the cached table information.
  26. */
  27. public function __construct(ViewsData $views_data) {
  28. $this->data = $views_data;
  29. }
  30. /**
  31. * Fetches a list of all fields available for a given base type.
  32. *
  33. * @param array|string $base
  34. * A list or a single base_table, for example node.
  35. * @param string $type
  36. * The handler type, for example field or filter.
  37. * @param bool $grouping
  38. * Should the result grouping by its 'group' label.
  39. * @param string $sub_type
  40. * An optional sub type. E.g. Allows making an area plugin available for
  41. * header only, instead of header, footer, and empty regions.
  42. *
  43. * @return array
  44. * A keyed array of in the form of 'base_table' => 'Description'.
  45. */
  46. public function fetchFields($base, $type, $grouping = FALSE, $sub_type = NULL) {
  47. if (!$this->fields) {
  48. $data = $this->data->get();
  49. // This constructs this ginormous multi dimensional array to
  50. // collect the important data about fields. In the end,
  51. // the structure looks a bit like this (using nid as an example)
  52. // $strings['nid']['filter']['title'] = 'string'.
  53. //
  54. // This is constructed this way because the above referenced strings
  55. // can appear in different places in the actual data structure so that
  56. // the data doesn't have to be repeated a lot. This essentially lets
  57. // each field have a cheap kind of inheritance.
  58. foreach ($data as $table => $table_data) {
  59. $bases = [];
  60. $strings = [];
  61. $skip_bases = [];
  62. foreach ($table_data as $field => $info) {
  63. // Collect table data from this table
  64. if ($field == 'table') {
  65. // calculate what tables this table can join to.
  66. if (!empty($info['join'])) {
  67. $bases = array_keys($info['join']);
  68. }
  69. // And it obviously joins to itself.
  70. $bases[] = $table;
  71. continue;
  72. }
  73. foreach (['field', 'sort', 'filter', 'argument', 'relationship', 'area'] as $key) {
  74. if (!empty($info[$key])) {
  75. if ($grouping && !empty($info[$key]['no group by'])) {
  76. continue;
  77. }
  78. if ($sub_type && isset($info[$key]['sub_type']) && (!in_array($sub_type, (array) $info[$key]['sub_type']))) {
  79. continue;
  80. }
  81. if (!empty($info[$key]['skip base'])) {
  82. foreach ((array) $info[$key]['skip base'] as $base_name) {
  83. $skip_bases[$field][$key][$base_name] = TRUE;
  84. }
  85. }
  86. elseif (!empty($info['skip base'])) {
  87. foreach ((array) $info['skip base'] as $base_name) {
  88. $skip_bases[$field][$key][$base_name] = TRUE;
  89. }
  90. }
  91. foreach (['title', 'group', 'help', 'base', 'aliases'] as $string) {
  92. // First, try the lowest possible level
  93. if (!empty($info[$key][$string])) {
  94. $strings[$field][$key][$string] = $info[$key][$string];
  95. }
  96. // Then try the field level
  97. elseif (!empty($info[$string])) {
  98. $strings[$field][$key][$string] = $info[$string];
  99. }
  100. // Finally, try the table level
  101. elseif (!empty($table_data['table'][$string])) {
  102. $strings[$field][$key][$string] = $table_data['table'][$string];
  103. }
  104. // We don't have any help provided for this field. If a better
  105. // description should be used for the Views UI you use
  106. // hook_views_data_alter() in module.views.inc or implement a
  107. // custom entity views_data handler.
  108. // @see hook_views_data_alter()
  109. // @see \Drupal\node\NodeViewsData
  110. elseif ($string == 'help') {
  111. $strings[$field][$key][$string] = '';
  112. }
  113. else {
  114. if ($string != 'base') {
  115. $strings[$field][$key][$string] = SafeMarkup::format("Error: missing @component", ['@component' => $string]);
  116. }
  117. }
  118. }
  119. }
  120. }
  121. }
  122. foreach ($bases as $base_name) {
  123. foreach ($strings as $field => $field_strings) {
  124. foreach ($field_strings as $type_name => $type_strings) {
  125. if (empty($skip_bases[$field][$type_name][$base_name])) {
  126. $this->fields[$base_name][$type_name]["$table.$field"] = $type_strings;
  127. }
  128. }
  129. }
  130. }
  131. }
  132. }
  133. // If we have an array of base tables available, go through them
  134. // all and add them together. Duplicate keys will be lost and that's
  135. // Just Fine.
  136. if (is_array($base)) {
  137. $strings = [];
  138. foreach ($base as $base_table) {
  139. if (isset($this->fields[$base_table][$type])) {
  140. $strings += $this->fields[$base_table][$type];
  141. }
  142. }
  143. uasort($strings, ['self', 'fetchedFieldSort']);
  144. return $strings;
  145. }
  146. if (isset($this->fields[$base][$type])) {
  147. uasort($this->fields[$base][$type], [$this, 'fetchedFieldSort']);
  148. return $this->fields[$base][$type];
  149. }
  150. return [];
  151. }
  152. /**
  153. * Sort function for fetched fields.
  154. *
  155. * @param array $a
  156. * First item for comparison. The compared items should be associative arrays
  157. * that include a 'group' and a 'title' key.
  158. * @param array $b
  159. * Second item for comparison.
  160. *
  161. * @return int
  162. * Returns -1 if $a comes before $b, 1 other way round and 0 if it cannot be
  163. * decided.
  164. */
  165. protected static function fetchedFieldSort($a, $b) {
  166. $a_group = Unicode::strtolower($a['group']);
  167. $b_group = Unicode::strtolower($b['group']);
  168. if ($a_group != $b_group) {
  169. return $a_group < $b_group ? -1 : 1;
  170. }
  171. $a_title = Unicode::strtolower($a['title']);
  172. $b_title = Unicode::strtolower($b['title']);
  173. if ($a_title != $b_title) {
  174. return $a_title < $b_title ? -1 : 1;
  175. }
  176. return 0;
  177. }
  178. }