JoinPluginBase.php 8.07 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\views\Plugin\views\join\JoinPluginBase.
6 7
 */

8
namespace Drupal\views\Plugin\views\join;
9

10
use Drupal\Core\Plugin\PluginBase;
11 12 13 14 15 16

/**
 * @defgroup views_join_handlers Views join handlers
 * @{
 * Handlers to tell Views how to join tables together.
 *
17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * Here is an example how to join from table one to example two so it produces
 * the following sql:
 * @code
 * INNER JOIN {two} ON one.field_a = two.field_b
 * @code.
 * The required php code for this kind of functionality is the following:
 * @code
 * $configuration = array(
 *   'table' => 'two',
 *   'field' => 'field_b',
 *   'left_table' => 'one',
 *   'left_field' => 'field_a',
 *   'operator' => '='
 * );
31
 * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
32
 *
33
 * To do complex joins:
34 35
 *
 * @code
36 37 38
 * class JoinComplex extends JoinPluginBase {
 *   public function buildJoin($select_query, $table, $view_query) {
 *     // Add an additional hardcoded condition to the query.
39
 *     $this->extra = 'foo.bar = baz.boing';
40
 *     parent::buildJoin($select_query, $table, $view_query);
41 42 43 44 45 46
 *   }
 * }
 * @endcode
 */

/**
47
 * Represents a join and creates the SQL necessary to implement the join.
48 49 50
 *
 * Extensions of this class can be used to create more interesting joins.
 */
51
class JoinPluginBase extends PluginBase {
52

53 54 55 56 57 58
  /**
   * The table to join (right table).
   *
   * @var string
   */
  public $table;
59

60 61 62 63 64 65
  /**
   * The field to join on (right field).
   *
   * @var string
   */
  public $field;
66

67 68 69 70 71 72
  /**
   * The table we join to.
   *
   * @var string
   */
  public $leftTable;
73

74 75 76 77 78 79
  /**
   * The field we join to.
   *
   * @var string
   */
  public $leftField;
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
  /**
   * An array of extra conditions on the join.
   *
   * Each condition is either a string that's directly added, or an array of
   * items:
   *   - table(optional): If not set, current table; if NULL, no table. If you
   *     specify a table in cached configuration, Views will try to load from an
   *     existing alias. If you use realtime joins, it works better.
   *   - field(optional): Field or formula. In formulas we can reference the
   *     right table by using %alias.
   *   - operator(optional): The operator used, Defaults to "=".
   *   - value: Must be set. If an array, operator will be defaulted to IN.
   *   - numeric: If true, the value will not be surrounded in quotes.
   *
   * @see SelectQueryInterface::addJoin()
   *
   * @var array
   */
  public $extra;
100

101 102 103 104 105 106
  /**
   * The join type, so for example LEFT (default) or INNER.
   *
   * @var string
   */
  public $type;
107

108 109 110 111 112
  /**
   * The configuration array passed by initJoin.
   *
   * @var array
   *
113
   * @see \Drupal\views\Plugin\views\join\JoinPluginBase::initJoin()
114 115
   */
  public $configuration = array();
116 117

  /**
118 119 120
   * How all the extras will be combined. Either AND or OR.
   *
   * @var string
121
   */
122 123 124 125 126 127 128 129 130 131 132 133 134
  public $extraOperator;

  /**
   * Defines whether a join has been adjusted.
   *
   * Views updates the join object to set the table alias instead of the table
   * name. Once views has changed the alias it sets the adjusted value so it
   * does not have to be updated anymore. If you create your own join object
   * you should set the adjusted in the definition array to TRUE if you already
   * know the table alias.
   *
   * @var bool
   *
135 136 137
   * @see \Drupal\views\Plugin\HandlerBase::getTableJoin()
   * @see \Drupal\views\Plugin\views\query\Sql::adjustJoin()
   * @see \Drupal\views\Plugin\views\relationship\RelationshipPluginBase::query()
138 139 140 141 142 143
   */
  public $adjusted;

  /**
   * Constructs a Drupal\views\Plugin\views\join\JoinPluginBase object.
   */
144
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
145
    parent::__construct($configuration, $plugin_id, $plugin_definition);
146 147 148 149 150 151 152 153 154
    // Merge in some default values.
    $configuration += array(
      'type' => 'LEFT',
      'extra_operator' => 'AND'
    );
    $this->configuration = $configuration;

    if (!empty($configuration['table'])) {
      $this->table = $configuration['table'];
155 156
    }

157 158 159 160 161 162
    $this->leftTable = $configuration['left_table'];
    $this->leftField = $configuration['left_field'];
    $this->field = $configuration['field'];

    if (!empty($configuration['extra'])) {
      $this->extra = $configuration['extra'];
163
    }
164 165

    if (isset($configuration['adjusted'])) {
166
      $this->adjusted = $configuration['adjusted'];
167 168 169 170
    }

    $this->extraOperator = strtoupper($configuration['extra_operator']);
    $this->type = $configuration['type'];
171 172 173
  }

  /**
174
   * Builds the SQL for the join this object represents.
175 176 177 178
   *
   * When possible, try to use table alias instead of table names.
   *
   * @param $select_query
179
   *   An select query object.
180 181
   * @param $table
   *   The base table to join.
182 183
   * @param \Drupal\views\Plugin\views\query\QueryPluginBase $view_query
   *   The source views query.
184
   */
185 186
  public function buildJoin($select_query, $table, $view_query) {
    if (empty($this->configuration['table formula'])) {
187 188 189
      $right_table = $this->table;
    }
    else {
190
      $right_table = $this->configuration['table formula'];
191 192
    }

193
    if ($this->leftTable) {
194
      $left = $view_query->getTableInfo($this->leftTable);
195
      $left_field = "$left[alias].$this->leftField";
196 197 198
    }
    else {
      // This can be used if left_field is a formula or something. It should be used only *very* rarely.
199
      $left_field = $this->leftField;
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    }

    $condition = "$left_field = $table[alias].$this->field";
    $arguments = array();

    // Tack on the extra.
    if (isset($this->extra)) {
      if (is_array($this->extra)) {
        $extras = array();
        foreach ($this->extra as $info) {
          // Figure out the table name. Remember, only use aliases provided
          // if at all possible.
          $join_table = '';
          if (!array_key_exists('table', $info)) {
            $join_table = $table['alias'] . '.';
          }
          elseif (isset($info['table'])) {
            // If we're aware of a table alias for this table, use the table
            // alias instead of the table name.
            if (isset($left) && $left['table'] == $info['table']) {
              $join_table = $left['alias'] . '.';
            }
            else {
              $join_table = $info['table'] . '.';
            }
          }

          // Convert a single-valued array of values to the single-value case,
          // and transform from IN() notation to = notation
          if (is_array($info['value']) && count($info['value']) == 1) {
            if (empty($info['operator'])) {
              $operator = '=';
            }
            else {
              $operator = $info['operator'] == 'NOT IN' ? '!=' : '=';
            }
            $info['value'] = array_shift($info['value']);
          }

          if (is_array($info['value'])) {
            // With an array of values, we need multiple placeholders and the
            // 'IN' operator is implicit.
            foreach ($info['value'] as $value) {
              $placeholder_i = ':views_join_condition_' . $select_query->nextPlaceholder();
              $arguments[$placeholder_i] = $value;
            }

            $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
            $placeholder = '( ' . implode(', ', array_keys($arguments)) . ' )';
          }
          else {
            // With a single value, the '=' operator is implicit.
            $operator = !empty($info['operator']) ? $info['operator'] : '=';
            $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
            $arguments[$placeholder] = $info['value'];
          }

          $extras[] = "$join_table$info[field] $operator $placeholder";
        }

        if ($extras) {
          if (count($extras) == 1) {
            $condition .= ' AND ' . array_shift($extras);
          }
          else {
265
            $condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')';
266 267 268 269 270 271 272 273 274 275
          }
        }
      }
      elseif ($this->extra && is_string($this->extra)) {
        $condition .= " AND ($this->extra)";
      }
    }

    $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
  }
276

277 278 279 280 281
}

/**
 * @}
 */