ViewsDataHelper.php 6.58 KB
Newer Older
1 2 3 4 5
<?php

namespace Drupal\views;

use Drupal\Component\Utility\Unicode;
6
use Drupal\Component\Utility\SafeMarkup;
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

/**
 * Defines a helper class for stuff related to views data.
 */
class ViewsDataHelper {

  /**
   * The views data object, containing the cached information.
   *
   * @var \Drupal\views\ViewsData
   */
  protected $data;

  /**
   * A prepared list of all fields, keyed by base_table and handler type.
   *
   * @param array
   */
  protected $fields;

  /**
   * Constructs a ViewsData object.
   *
   * @param \Drupal\views\ViewsData $views_data
   *   The views data object, containing the cached table information.
   */
  public function __construct(ViewsData $views_data) {
    $this->data = $views_data;
  }

  /**
   * Fetches a list of all fields available for a given base type.
   *
40
   * @param array|string $base
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
   *   A list or a single base_table, for example node.
   * @param string $type
   *   The handler type, for example field or filter.
   * @param bool $grouping
   *   Should the result grouping by its 'group' label.
   * @param string $sub_type
   *   An optional sub type. E.g. Allows making an area plugin available for
   *   header only, instead of header, footer, and empty regions.
   *
   * @return array
   *   A keyed array of in the form of 'base_table' => 'Description'.
   */
  public function fetchFields($base, $type, $grouping = FALSE, $sub_type = NULL) {
    if (!$this->fields) {
      $data = $this->data->get();
      // This constructs this ginormous multi dimensional array to
      // collect the important data about fields. In the end,
      // the structure looks a bit like this (using nid as an example)
      // $strings['nid']['filter']['title'] = 'string'.
      //
      // This is constructed this way because the above referenced strings
      // can appear in different places in the actual data structure so that
      // the data doesn't have to be repeated a lot. This essentially lets
      // each field have a cheap kind of inheritance.

      foreach ($data as $table => $table_data) {
67 68 69
        $bases = [];
        $strings = [];
        $skip_bases = [];
70 71 72 73 74 75 76 77 78 79 80
        foreach ($table_data as $field => $info) {
          // Collect table data from this table
          if ($field == 'table') {
            // calculate what tables this table can join to.
            if (!empty($info['join'])) {
              $bases = array_keys($info['join']);
            }
            // And it obviously joins to itself.
            $bases[] = $table;
            continue;
          }
81
          foreach (['field', 'sort', 'filter', 'argument', 'relationship', 'area'] as $key) {
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
            if (!empty($info[$key])) {
              if ($grouping && !empty($info[$key]['no group by'])) {
                continue;
              }
              if ($sub_type && isset($info[$key]['sub_type']) && (!in_array($sub_type, (array) $info[$key]['sub_type']))) {
                continue;
              }
              if (!empty($info[$key]['skip base'])) {
                foreach ((array) $info[$key]['skip base'] as $base_name) {
                  $skip_bases[$field][$key][$base_name] = TRUE;
                }
              }
              elseif (!empty($info['skip base'])) {
                foreach ((array) $info['skip base'] as $base_name) {
                  $skip_bases[$field][$key][$base_name] = TRUE;
                }
              }
99
              foreach (['title', 'group', 'help', 'base', 'aliases'] as $string) {
100 101 102 103 104 105 106 107 108 109 110 111
                // First, try the lowest possible level
                if (!empty($info[$key][$string])) {
                  $strings[$field][$key][$string] = $info[$key][$string];
                }
                // Then try the field level
                elseif (!empty($info[$string])) {
                  $strings[$field][$key][$string] = $info[$string];
                }
                // Finally, try the table level
                elseif (!empty($table_data['table'][$string])) {
                  $strings[$field][$key][$string] = $table_data['table'][$string];
                }
112 113 114 115 116 117 118 119 120
                // We don't have any help provided for this field. If a better
                // description should be used for the Views UI you use
                // hook_views_data_alter() in module.views.inc or implement a
                // custom entity views_data handler.
                // @see hook_views_data_alter()
                // @see \Drupal\node\NodeViewsData
                elseif ($string == 'help') {
                  $strings[$field][$key][$string] = '';
                }
121
                else {
122
                  if ($string != 'base') {
123
                    $strings[$field][$key][$string] = SafeMarkup::format("Error: missing @component", ['@component' => $string]);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
                  }
                }
              }
            }
          }
        }
        foreach ($bases as $base_name) {
          foreach ($strings as $field => $field_strings) {
            foreach ($field_strings as $type_name => $type_strings) {
              if (empty($skip_bases[$field][$type_name][$base_name])) {
                $this->fields[$base_name][$type_name]["$table.$field"] = $type_strings;
              }
            }
          }
        }
      }
    }

    // If we have an array of base tables available, go through them
    // all and add them together. Duplicate keys will be lost and that's
    // Just Fine.
    if (is_array($base)) {
146
      $strings = [];
147 148 149 150 151
      foreach ($base as $base_table) {
        if (isset($this->fields[$base_table][$type])) {
          $strings += $this->fields[$base_table][$type];
        }
      }
152
      uasort($strings, ['self', 'fetchedFieldSort']);
153 154 155 156
      return $strings;
    }

    if (isset($this->fields[$base][$type])) {
157
      uasort($this->fields[$base][$type], [$this, 'fetchedFieldSort']);
158 159
      return $this->fields[$base][$type];
    }
160
    return [];
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
  }

  /**
   * Sort function for fetched fields.
   *
   * @param array $a
   *   First item for comparison. The compared items should be associative arrays
   *   that include a 'group' and a 'title' key.
   * @param array $b
   *   Second item for comparison.
   *
   * @return int
   *   Returns -1 if $a comes before $b, 1 other way round and 0 if it cannot be
   *   decided.
   */
  protected static function fetchedFieldSort($a, $b) {
    $a_group = Unicode::strtolower($a['group']);
    $b_group = Unicode::strtolower($b['group']);
    if ($a_group != $b_group) {
      return $a_group < $b_group ? -1 : 1;
    }

    $a_title = Unicode::strtolower($a['title']);
    $b_title = Unicode::strtolower($b['title']);
    if ($a_title != $b_title) {
      return $a_title < $b_title ? -1 : 1;
    }

    return 0;
  }

}