You are here

class _views_query in Views (for Drupal 7) 5

Hierarchy

Expanded class hierarchy of _views_query

File

./views_query.inc, line 282

View source
class _views_query {

  /*
   * Create the basic query object and fill with default values.
   */
  function _views_query($primary_table = 'node', $primary_field = 'nid', $alias_prefix = '') {
    $this->primary_table = $primary_table;
    $this->primary_field = $primary_field;
    $this->joins = array();
    $this->where = array();
    $this->orderby = array();
    $this->groupby = array();
    $this->tables = array();
    $this->where_args = array();
    $this->use_alias_prefix = $alias_prefix;

    // Joins care about order, so we put our tables in a queue to make sure
    // the order is correct.
    $this->tablequeue = array();
    if ($primary_field) {
      $this->fields = array(
        $alias_prefix . "{$primary_table}.{$primary_field}",
      );
    }
    $this->count_field = $alias_prefix . "{$primary_table}.{$primary_field}";
    $this->header = array();
  }

  /*
   * Add a field to the query table, possibly with an alias. This will
   * automatically call ensure_table to make sure the required table
   * exists, *unless* $table is unset.
   */
  function add_field($field, $table = '$$', $alias = '') {
    if ($table == '$$') {

      // I picked $$ because it's not a legal name for a table and NULL is
      // actually a valid possibility here, and I can't default to a variable.
      $table = $this->primary_table;
    }

    // We check for this specifically because we don't want to add it aliased.
    if ($table == $this->primary_table && $field == $this->primary_field) {
      return;
    }
    if ($table) {
      $this
        ->ensure_table($table);
      $table = $this->use_alias_prefix . $table . ".";
    }
    if ($alias) {
      $a = " AS {$this->use_alias_prefix}{$alias}";
    }
    if (!in_array("{$table}{$field}{$a}", $this->fields)) {
      $this->fields[] = "{$table}{$field}{$a}";
    }
  }

  /*
   * Set the base field to be distinct.
   */
  function set_distinct($value = TRUE) {
    if (!($this->no_distinct && $value)) {
      $this->distinct = $value;
    }
  }

  /*
   * Remove all fields that may've been added; primarily used for summary
   * mode where we're changing the query because we didn't get data we needed.
   */
  function clear_fields() {
    $this->fields = array();
  }

  /*
   * Set what field the query will count() on for paging.
   */
  function set_count_field($field) {
    $this->count_field = $field;
  }

  /*
   * Add a simple WHERE clause to the query. The caller is responsible for
   * ensuring that all fields are fully qualified (TABLE.FIELD) and that
   * the table already exists in the query.
   */
  function add_where($clause) {
    $args = func_get_args();
    array_shift($args);

    // ditch $clause
    if (count($args) == 1 && is_array(reset($args))) {
      $args = current($args);
    }
    if (is_array($args)) {
      $this->where[] = $clause;
      $this->where_args = array_merge($this->where_args, $args);
    }
  }

  /*
   * Add multiple an orderby's. Right now I'm not sure why this function
   * is separate from add_orderby above; I don't think it needs to
   * be.
   *
   * NOTE: $alias doesn't work when adding multiple orderbys.
   */
  function add_orderby($table, $field, $order, $alias = '') {
    if (!$alias && $table) {
      $alias = $this->use_alias_prefix . $table;
    }
    elseif ($alias) {
      $alias = $this->use_alias_prefix . $alias;
    }
    if ($table) {
      $this
        ->ensure_table($table);
    }
    if (!is_array($field)) {
      $field = array(
        $field,
      );
    }
    foreach ($field as $f) {

      // Only fill out this aliasing if there is a table;
      // otherwise we assume it is a formula.
      if ($table) {
        $as = $alias . '_' . $f;
      }
      else {
        $as = $alias;
      }
      $this
        ->add_field($f, $table, $as);
      $this->orderby[] = "{$as} {$order}";
      if ($this->groupby) {
        $this
          ->add_groupby($as);
      }
    }
  }

  /*
   * Add a simple GROUP BY clause to the query. The caller is responsible
   * for ensuring that the fields are fully qualified and the table is properly
   * added.
   */
  function add_groupby($clause) {
    $this->groupby[] = $clause;
  }

  /*
   * This function will ensure a table exists; if it already exists it won't
   * do anything, but if it doesn't it will add the table queue. It will ensure
   * a path leads back to the views_get_title table.
   */
  function ensure_table($table) {
    if ($table == $this->primary_table || $this->tables[$table]) {
      return;
    }
    if ($this
      ->ensure_path($table)) {
      $this
        ->queue_table($table);
    }
  }

  /*
   * This function will add a table to the query.
   *
   * @param $table
   *   The name of the table to add. It needs to exist in the global table
   *   array.
   * @param $ensure_path
   *   If true, it will ensure that a path back to primary_table exists. If false, it
   *   assumes the caller is already sure about this and does not repeat it.
   * @param $howmany
   *   How many instances of the table to add? (Useful for interesting join
   *   combos)
   * @param $joininfo
   *   In some join configurations this table may actually join back through
   *   a different method; this is most likely to be used when tracing
   *   a hierarchy path. (node->parent->parent2->parent3). This parameter
   *   will specify how this table joins if it is not the default.
   */
  function add_table($table, $ensure_path = false, $howmany = 1, $joininfo = NULL) {
    if ($table == $this->primary_table) {
      return;
    }
    if ($ensure_path && !$this
      ->ensure_path($table)) {
      return false;
    }
    $this->tables[$table] += $howmany;
    for ($i = $this->tables[$table] - $howmany + 1; $i <= $this->tables[$table]; $i++) {
      if ($joininfo) {
        $this->joins[$table][$i] = $joininfo;
      }
      $this->tablequeue[] = array(
        'table' => $table,
        'num' => $i,
        'alias_prefix' => $this->use_alias_prefix,
      );
    }
    return $this->tables[$table];
  }

  /*
   * Add a table to the queue in the specified position. This is a low level
   * operation and should only be performed after all safety checking has been done.
   */
  function queue_table($table) {
    if (!isset($this->tables[$table])) {
      $this->tables[$table] = 1;
    }
    else {
      $this->tables[$table]++;
    }
    $this->tablequeue[] = array(
      'table' => $table,
      'num' => $this->tables[$table],
      'alias_prefix' => $this->use_alias_prefix,
    );
  }

  /*
   * Set the table header; used for click-sorting because it's needed
   * info to modify the ORDER BY clause.
   */
  function set_header($header) {
    $this->header = $header;
  }

  /*
   * Make sure that the specified table can be properly linked to the views_get_title
   * table in the JOINs. This function uses recursion. If the tables
   * needed to complete the path back to the views_get_title table are not in the
   * query they will be added, but additional copies will NOT be added
   * if the table is already there.
   */
  function ensure_path($table, $traced = array(), $add = array()) {
    if ($table == $this->primary_table) {
      return true;
    }
    $table_data = _views_get_tables();
    $left_table = $table_data[$table]['join']['left']['table'];

    // Does it end at our views_get_title table?
    if ($left_table == $this->primary_table) {

      // We are done! Add our tables and unwind.
      foreach (array_reverse($add) as $table) {
        $this
          ->queue_table($table);
      }
      return true;
    }

    // Have we been this way?
    if (isset($traced[$left_table])) {

      // we looped. Broked.
      return false;
    }

    // Do we have to add this table?
    if (!isset($this->tables[$left_table])) {
      $add[] = $left_table;
    }

    // Keep looking.
    $traced[$left_table] = 1;
    return $this
      ->ensure_path($left_table, $traced, $add);
  }

  /*
   * Return the alias of a given table in the JOIN queries. If a table is in
   * the query multiple times, use this function to figure out what successive
   * iterations of the table will be named.
   *
   * @param $table
   *   The name of the table in the global table array.
   * @param $table_num
   *   The instance number of the table.
   * @param $alias_prefix
   *   An optional prefix for the table alias.
   */
  function get_table_name($table, $table_num, $alias_prefix = null) {
    if (is_null($alias_prefix)) {
      $alias_prefix = $this->use_alias_prefix;
    }
    return $table_num < 2 ? $alias_prefix . $table : $alias_prefix . $table . $table_num;
  }

  /*
   * Generate a query and a countquery from all of the information supplied
   * to the object.
   *
   * @param $getcount
   *   Provide a countquery if this is true, otherwise provide a normal query.
   */
  function query($getcount = false) {
    $table_data = _views_get_tables();

    // Set distinct
    if (!$this->no_distinct && $this->distinct && count($this->fields)) {
      $field = $this->fields[0];
      $this->fields[0] = "DISTINCT({$field})";
      $this->count_field = "DISTINCT({$this->count_field})";

      //this is only needed once
      $this->no_distinct = TRUE;
    }

    // Add all the tables to the query via joins. We assume all LEFT joins.
    foreach ($this->tablequeue as $tinfo) {
      $table = $tinfo['table'];

      // The real table name may differ from what we're calling it.
      $table_real = isset($table_data[$table]['name']) ? $table_data[$table]['name'] : $table;
      $table_num = $tinfo['num'];
      $table_alias = $this
        ->get_table_name($table, $table_num, $tinfo['alias_prefix']);
      $joininfo = !$this->joins[$table][$table_num] ? $table_data[$table]['join'] : $this->joins[$table][$table_num];
      $left_table_alias = isset($joininfo['left']['alias']) ? $joininfo['left']['alias'] : $tinfo['alias_prefix'];
      $left_table_alias .= $joininfo['left']['table'];

      // the { is a special character which seems to be treated differently
      // in PHP5 than PHP4 so we do this a little oddly.
      $join_type = $joininfo['type'] == 'inner' ? 'INNER' : 'LEFT';
      $joins .= " {$join_type} JOIN {" . $table_real . "} {$table_alias} ON " . $left_table_alias . "." . $joininfo['left']['field'] . " = {$table_alias}." . $joininfo['right']['field'];
      if (isset($joininfo['extra'])) {
        foreach ($joininfo['extra'] as $field => $value) {
          $joins .= " AND {$table_alias}.{$field}";
          if (is_array($value) && count($value)) {
            $joins .= " IN ('" . implode("','", $value) . "')";
          }
          else {
            if ($value !== NULL) {
              $joins .= " = '{$value}'";
            }
          }
        }
      }
    }

    // If it's not a count query, add our fields
    if (!$getcount) {
      $fields = implode(', ', $this->fields);

      // we only add the groupby if we're not counting.
      if ($this->groupby) {
        $groupby = "GROUP BY " . implode(', ', $this->groupby);
      }

      // ok, tablesort_sql is really, really (really) annoying.
      // 1) it insists on adding the ORDER BY clause.
      // 2) You're supposed to give it your stuff as $before, but
      // 3) You have to add the comma and
      // 4) if it doesn't have anything to add, it returns NOTHING.
      // 5) So I'm just going to get what it sends back and chop off
      //    the orderby, cause otherwise my code is too ugly with
      //    various checks.
      if ($this->header) {
        $result = tablesort_sql($this->header);
        if ($result) {
          $this->orderby[] = str_replace('ORDER BY ', '', $result);
        }
      }
      if ($this->orderby) {
        $orderby = "ORDER BY " . implode(', ', $this->orderby);
      }
    }
    else {
      $fields = "count({$this->count_field})";
    }
    if ($this->where) {
      $where = "WHERE (" . implode(') AND (', $this->where) . ')';
    }
    $query = "SELECT {$fields} FROM {" . $this->primary_table . "} {$this->primary_table} {$joins} {$where} {$groupby} {$orderby}";
    $replace = array(
      '&gt;' => '>',
      '&lt;' => '<',
    );
    $query = strtr($query, $replace);
    return $query;
  }

}

Members