You are here

class DataTable in Data 6

Same name and namespace in other branches
  1. 7 includes/DataTable.inc \DataTable

Manages data access and manipulation for a single data table. Use data_create_table() or data_get_table() to instantiate an object from this class.

Usage:

Get an existing table.

$table = data_get_table('my_table');

If the table does not exist, create one. if (!$table) { $table = data_create_table('my_table', $schema); }

Save some data to it. $handler = data_get_handler($table->get('name')); $handler->save($data);

Remove the data from the table. $handler->truncate();

Remove the table, but not the meta information about the table. $table->drop();

Hierarchy

Expanded class hierarchy of DataTable

See also

data_create_table().

data_get_table().

3 string references to 'DataTable'
data_create_table in ./data.module
Create a table.
data_get_table in ./data.module
Get a table if it exists.
data_ui_adopt_form_submit in data_ui/data_ui.admin.inc
Submit handler for adopt table form.

File

includes/DataTable.inc, line 36
Contains class definition for DataTable.

View source
class DataTable {

  // Class variables.
  // @todo: change $table_schema to $schema.
  // @todo: change $name to $id.
  // Unfortunately drupal_write_record does not escape field names. $table_schema instead of $schema it is.
  protected $name, $title, $table_schema, $meta, $export_type;

  /**
   * Instiate a DataTable object. Use this function instead of new DataTable.
   */
  public static function instance($name) {
    static $tables;
    if (!isset($tables[$name])) {
      $tables[$name] = new DataTable($name);
    }
    return $tables[$name];
  }

  /**
   * Constructor. Do not call directly, but use DataTable::instance($name) instead.
   */
  protected function __construct($name) {
    $this->name = $name;

    // Try to load table information.
    if ($table = _data_load_table($name)) {
      foreach (array(
        'title',
        'name',
        'table_schema',
        'meta',
        'export_type',
      ) as $key) {
        if (isset($table->{$key})) {
          $this->{$key} = $table->{$key};
        }
      }
    }
  }

  /**
   * Create a table.
   *
   * Do not call directly but use data_create_table() instead.
   */
  public function create($table_schema) {

    // Only create the table if it is not defined as data table AND it does not
    // physically exist.
    if (!_data_load_table($this->name, TRUE) && !db_table_exists($this->name)) {

      // Create table.
      db_create_table($ret, $this->name, $table_schema);
      if ($ret[0]['success'] != 1) {
        drupal_set_message(t('Error creating table.'), 'error');
        return FALSE;
      }

      // If schema module is enabled, inspect and read back to make
      // sure our schema information is up to date.
      // @todo: this is slow, maybe we need to make this an explicit method
      // on DataTable.
      if (module_exists('schema')) {
        $schema = schema_invoke('inspect');
        if (isset($schema[$this->name])) {
          $table_schema = $schema[$this->name];
        }
      }

      // Set table_schema and export_type.
      // @todo: rather user _data_table_load() ?
      $this->table_schema = $table_schema;
      $this->export_type = EXPORT_IN_DATABASE;

      // Save table information.
      // Set export_type - needs to be defined so that schema information is being passed on
      // to Drupal by data_schema_alter().
      // @todo: refactor ->update() to ->save() and use ->save().
      $table = array(
        'name' => $this->name,
        'table_schema' => $this->table_schema,
      );
      drupal_write_record('data_tables', $table);

      // Clear caches.
      drupal_get_schema($this->name, TRUE);

      // Have views read new views information about table.
      if (module_exists('views')) {
        views_invalidate_cache();
      }

      // data ui exposes path to a new default view.
      if (module_exists('data_ui')) {
        menu_rebuild();
      }
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Let Data manage a table that already exists in the database.
   *
   * Uses the $name property of the object to determine which database table to
   * adopt.
   *
   * @return
   *   TRUE if the table was successfully adopted; FALSE if the table was
   *   already known to Data, if the query failed, or if Schema isn't available.
   */
  public function adopt() {
    if ($this
      ->defined() || !module_exists('schema')) {
      return FALSE;
    }
    $schema = schema_invoke('inspect', $this->name);
    if (isset($schema[$this->name])) {
      $table = array(
        'name' => $this->name,
        'title' => data_natural_name($this->name),
        'table_schema' => $schema[$this->name],
      );
      if (drupal_write_record('data_tables', $table)) {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Remove a table from Data module's management, ie unadopt it.
   *
   * Uses the $name property of the object to determine which database table to
   * adopt.
   *
   * @return
   *   TRUE if the table was successfully disowned; FALSE if the query failed,
   *   or if Schema isn't available.
   */
  public function disown() {
    $table = array(
      $this->name,
    );
    return db_query("DELETE FROM {data_tables} WHERE name IN (" . db_placeholders($table, 'varchar') . ");", $table);
  }

  /**
   * Determine whether a table is defined.
   *
   * @return
   *   TRUE if the table is defined, FALSE otherwise.
   *   Note: If a table is defined it does not mean that it actually exists in the
   *   database.
   */
  public function defined() {
    return _data_load_table($this->name) ? TRUE : FALSE;
  }

  /**
   * Get a property of the DataTable object.
   *
   * @todo: use __get()
   *
   * @param $property
   *   One of 'name', 'title', 'table_schema', 'meta'.
   * @return
   *   The unserialized value of the property.
   */
  public function get($property) {
    if (in_array($property, array(
      'name',
      'title',
      'table_schema',
      'meta',
      'export_type',
    ))) {
      return $this->{$property};
    }
  }

  /**
   * Update table properties.
   *
   * @todo: make conditional, rename to save().
   *
   * @param $properties
   *   Array where the key designates a property (one of 'name', 'title', 'table_schema', 'meta')
   *   and the value is the unserialized value that this property should attain.
   */
  public function update($properties) {
    _data_override($this->name);
    $properties['name'] = $this->name;
    if (drupal_write_record('data_tables', $properties, 'name')) {
      foreach ($properties as $key => $value) {
        $this->{$key} = $value;
      }
    }
  }

  /**
   * Compare this table's schema to schema of table in DB.
   * Requires schema module.
   *
   * @return
   *
   */
  public function compareSchema() {
    if (module_exists('schema')) {
      $this->table_schema['name'] = $this->name;
      return schema_compare_table($this->table_schema);
    }
  }

  /**
   * Add a field.
   *
   * @throws DataException
   *
   * @todo: Check wether field name is available, otherwise change.
   */
  public function addField($field, $spec) {
    $ret = array();
    db_add_field($ret, $this->name, $field, $spec);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      $schema['fields'][$field] = $spec;
      $this
        ->update(array(
        'table_schema' => $schema,
      ));

      // @todo: use clearCaches().
      drupal_get_schema($this->name, TRUE);

      // Invalidate views caches to use new field immediately.
      if (function_exists('views_invalidate_cache')) {
        views_invalidate_cache();
      }
      return $field;
    }
    throw new DataException(t('Error adding field.'));
  }

  /**
   * Add an index to table.
   *
   * @todo: support more than one field.
   */
  public function addIndex($field) {
    $schema = $this->table_schema;
    if ($schema['fields'][$field]) {
      $index = data_get_index_definition($field, $schema['fields'][$field]);
      db_add_index($ret, $this->name, $field, $index);
      if ($ret[0]['success']) {
        $schema['indexes'][$field] = $index;
        $this
          ->update(array(
          'table_schema' => $schema,
        ));
        drupal_get_schema($this->name, TRUE);
        return;
      }
    }
    throw new DataException(t('Error adding index.'));
  }

  /**
   * Drop an index from a table.
   *
   * @throws DataException
   */
  public function dropIndex($field) {
    $ret = array();
    db_drop_index($ret, $this->name, $field);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      unset($schema['indexes'][$field]);
      $this
        ->update(array(
        'table_schema' => $schema,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Error dropping index.'));
  }

  /**
   * Add a unique key to a field.
   *
   * @throws DataException
   */
  public function addUniqueKey($field) {
    $schema = $this->table_schema;
    if ($schema['fields'][$field]) {
      $ret = array();
      $index = data_get_index_definition($field, $schema['fields'][$field]);
      db_add_unique_key($ret, $this->name, $field, $index);
      if ($ret[0]['success']) {
        $schema['unique keys'][$field] = array(
          $field,
        );
        $this
          ->update(array(
          'table_schema' => $schema,
        ));
        drupal_get_schema($this->name, TRUE);
        return;
      }
    }
    throw new DataException(t('Error adding unique key.'));
  }

  /**
   * Drop a unique key from a table.
   *
   * @throws DataException
   */
  public function dropUniqueKey($field) {
    $ret = array();
    db_drop_unique_key($ret, $this->name, $field);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      unset($schema['unique keys'][$field]);
      $this
        ->update(array(
        'table_schema' => $schema,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Error dropping unique key.'));
  }

  /**
   * Change indexes of a table.
   *
   * @throws DataException
   */
  public function changeIndex($fields) {
    $schema = $this->table_schema;

    // @TODO: This array_keys() reduces indexes to single field indexes.
    // Will need adjustment when multi-field indexes are implemented.
    $indexes = isset($schema['indexes']) ? array_keys($schema['indexes']) : array();
    $add = array_diff($fields, $indexes);
    $drop = array_diff($indexes, $fields);
    foreach ($add as $field) {
      $this
        ->addIndex($field);
    }
    foreach ($drop as $field) {
      $this
        ->dropIndex($field);
    }
  }

  /**
   * Add a primary key to table.
   *
   * @throws DataException
   */
  public function addPrimaryKey($fields) {
    $ret = array();
    $schema = $this->table_schema;
    foreach ($fields as $field) {
      if ($schema['fields'][$field]['type'] == 'text') {
        throw new DataException(t('A text field cannot be made a primary key.'));
      }
    }
    db_add_primary_key($ret, $this->name, $fields);
    if ($ret[0]['success']) {
      $schema['primary key'] = $fields;
      $this
        ->update(array(
        'table_schema' => $schema,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Error creating primary key.'));
  }

  /**
   * Drop all primary keys from a table.
   *
   * @throws DataException
   */
  public function dropPrimaryKey() {
    $ret = array();
    db_drop_primary_key($ret, $this->name);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      $schema['primary key'] = array();
      $this
        ->update(array(
        'table_schema' => $schema,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Error dropping primary key.'));
  }

  /**
   * Change the primary keys of a table.
   *
   * @throws DataException
   */
  public function changePrimaryKey($fields) {
    $schema = $this->table_schema;
    if (!empty($schema['primary key'])) {
      $this
        ->dropPrimaryKey();
    }
    if (!empty($fields)) {
      $this
        ->addPrimaryKey($fields);
    }
  }

  /**
   * Change a field.
   *
   * @throws DataException
   */
  public function changeField($field, $spec) {
    $ret = array();

    // If new type is text, check for PK and index restrictions.
    if ($spec['type'] == 'text') {
      if (in_array($field, $this->table_schema['primary key'])) {
        throw new DataException(t('Cannot make a primary key field a text field.'));
      }
      foreach ($this->table_schema['indexes'] as $index_name => $index) {
        foreach ($index as $index_field) {
          if (is_array($index_field)) {
            $index_field = array_shift($index_field);
          }
          if ($field == $index_field) {
            $this
              ->dropIndex($index_field);
          }
        }
      }
    }
    db_change_field($ret, $this->name, $field, $field, $spec);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      $schema['fields'][$field] = $spec;
      $this
        ->update(array(
        'table_schema' => $schema,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Cannot change field.'));
  }

  /**
   * Delete a field.
   *
   * @throws DataException
   */
  public function dropField($field) {
    $ret = array();
    db_drop_field($ret, $this->name, $field);
    if ($ret[0]['success']) {
      $schema = $this->table_schema;
      unset($schema['fields'][$field]);
      $meta = $this->meta;
      unset($meta['fields'][$field]);
      $this
        ->update(array(
        'table_schema' => $schema,
      ), array(
        'meta' => $meta,
      ));
      drupal_get_schema($this->name, TRUE);
      return;
    }
    throw new DataException(t('Cannot drop field.'));
  }

  /**
   * Drop a table. Does not drop a table if its defined in code.
   *
   * @return
   *   TRUE if the table was dropped, FALSE otherwise.
   */
  public function drop() {
    if ($this->export_type == EXPORT_IN_DATABASE) {
      if (db_table_exists($this->name)) {
        db_drop_table($ret, $this->name);
      }
      $this
        ->update(array(
        'table_schema' => array(),
      ));
      drupal_get_schema($this->name, TRUE);
      db_query('DELETE FROM {data_tables} WHERE name = "%s"', $this->name);
      $this->title = '';
      $this->table_schema = $this->meta = array();
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Revert a table to its definition in code.
   *
   * Does not revert a table if it is not defined in code.
   *
   * @return
   *   TRUE if the table was reverted, FALSE otherwise.
   */
  public function revert() {
    if ($this->export_type & EXPORT_IN_CODE) {
      db_query('DELETE FROM {data_tables} WHERE name = "%s"', $this->name);
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Link this table to another table. Linking a table to another one is
   * to define how data in these tables should be joined to each other.
   *
   * There can be more than one link to the left of a table. However,
   * for views integration, only the first join created will be used.
   *
   * @todo: Get rid of link() language, use setJoin()/removeJoin() instead.
   */
  public function link($left_table, $left_field, $field = NULL, $inner_join = TRUE) {
    if ($field == NULL) {
      $field = $left_table;
    }
    $this->meta['join'][$left_table] = array(
      'left_field' => $left_field,
      'field' => $field,
      'inner_join' => $inner_join,
    );
    $this
      ->update(array(
      'meta' => $this->meta,
    ));
  }

  /**
   * Unlink this table from another table.
   */
  public function unlink($left_table) {
    unset($this->meta['join'][$left_table]);
    $this
      ->update(array(
      'meta' => $this->meta,
    ));
  }

  /**
   * Convenience method.
   */
  public function handler() {
    return data_get_handler($this->name);
  }

  /**
   * Clear relevant caches. Call after operations that create, delete or modify
   * tables.
   */
  public static function clearCaches() {

    // Clear the schema cache.
    drupal_get_schema(NULL, TRUE);

    // Have views read new views information about table.
    if (module_exists('views')) {
      views_invalidate_cache();
    }

    // data ui exposes path to a new default view.
    if (module_exists('data_ui')) {
      menu_rebuild();
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DataTable::$name protected property
DataTable::addField public function Add a field.
DataTable::addIndex public function Add an index to table.
DataTable::addPrimaryKey public function Add a primary key to table.
DataTable::addUniqueKey public function Add a unique key to a field.
DataTable::adopt public function Let Data manage a table that already exists in the database.
DataTable::changeField public function Change a field.
DataTable::changeIndex public function Change indexes of a table.
DataTable::changePrimaryKey public function Change the primary keys of a table.
DataTable::clearCaches public static function Clear relevant caches. Call after operations that create, delete or modify tables.
DataTable::compareSchema public function Compare this table's schema to schema of table in DB. Requires schema module.
DataTable::create public function Create a table.
DataTable::defined public function Determine whether a table is defined.
DataTable::disown public function Remove a table from Data module's management, ie unadopt it.
DataTable::drop public function Drop a table. Does not drop a table if its defined in code.
DataTable::dropField public function Delete a field.
DataTable::dropIndex public function Drop an index from a table.
DataTable::dropPrimaryKey public function Drop all primary keys from a table.
DataTable::dropUniqueKey public function Drop a unique key from a table.
DataTable::get public function Get a property of the DataTable object.
DataTable::handler public function Convenience method.
DataTable::instance public static function Instiate a DataTable object. Use this function instead of new DataTable.
DataTable::link public function Link this table to another table. Linking a table to another one is to define how data in these tables should be joined to each other.
DataTable::revert public function Revert a table to its definition in code.
DataTable::unlink public function Unlink this table from another table.
DataTable::update public function Update table properties.
DataTable::__construct protected function Constructor. Do not call directly, but use DataTable::instance($name) instead.