diff --git a/data.module b/data.module index 058b92085194bbc2928050fab011d8ed08b34b9f..7945e3631c6a42c16ce2504b656eebe31ca90760 100644 --- a/data.module +++ b/data.module @@ -76,12 +76,20 @@ function data_drop_table($name) { /** * Internal singleton/factory function for creating a single instance of a DataTable class. * + * Don't use this function directly. Call data_create_table() or data_get_table() instead. + * * If a schema is given, _data_get_table() creates the table objects DB structure. * - * Don't use this function directly. Call data_create_table() or data_get_table() instead. + * The purpose of this function is to make sure that + * + * a) there is only a single DataTable object for accessing a specific DataTable. + * b) there is no DataTable object that does not have an existing table. */ function _data_get_table($name, $schema = NULL, $title = NULL, $reset = FALSE) { static $tables; + // Simple way of having a way to override the class being used. + // This could be refined with a $type parameter in _data_get_table() and depending + // functions. $class = variable_get('data_table_class', 'DataTable'); if ($reset) { unset($tables[$name]); @@ -126,13 +134,13 @@ function data_get_all_tables() { } /** - * Get a list of available, simplified field types. + * Get a list of supported field definitions. * * This list is a sub set of Schema API data types * http://drupal.org/node/159605 * The keys are simplified handles. */ -function data_get_field_types() { +function data_get_field_definitions() { return array( 'int' => array( 'type' => 'int', @@ -156,18 +164,55 @@ function data_get_field_types() { } /** - * Translate a simplified handle into a schema API type definition. + * Get a definition key into a schema API type definition. * * If no type can be found, FALSE will be returned. */ -function data_translate_field_type($type) { - $types = data_get_field_types(); - if (isset($types[$type])) { - return $types[$type]; +function data_get_field_definition($key) { + $definitions = data_get_field_definitions(); + if (isset($definitions[$key])) { + return $definitions[$key]; } return FALSE; } +/** + * Get schema API field types supported by Data module. + */ +function data_get_field_types() { + $definitions = data_get_field_definitions(); + $types = array(); + foreach ($definitions as $def) { + $types[$def['type']] = $def['type']; + } + return $types; +} + +/** + * Get a Schema API PK definition for a given field type. + */ +function data_get_pk_definition($name, $type) { + if ($type == 'text') { + return array($name, 255); + } + else { + return $name; + } +} + +/** + * Get a Schema API index definition for a given field type. + * @todo: support multiple name/type combinations. + */ +function data_get_index_definition($name, $type) { + if ($type == 'text') { + return array(array($name, 255)); + } + else { + return array($name); + } +} + /** * Create a table name in the data namespace. * @todo: make overridable. @@ -234,17 +279,15 @@ function data_get_default_path($name) { * @todo: may be add option to add a full fledged schema here? */ function data_build_schema($keys) { - $fields = $schema = array(); - $field_types = data_get_field_types(); - // Build the table definition. // Fall back to varchar if no valid type is given. - foreach ($keys as $k => $type) { - if ($type = data_translate_field_type($type)) { - $fields[data_safe_name($k)] = $type; + $fields = $schema = array(); + foreach ($keys as $k => $key) { + if ($definition = data_get_field_definition($key)) { + $fields[data_safe_name($k)] = $definition; } else { - $fields[data_safe_name($k)] = $field_types['varchar']; + $fields[data_safe_name($k)] = data_get_field_definition('varchar'); } } @@ -254,12 +297,12 @@ function data_build_schema($keys) { } /** - * Build a full schema api field spec. + * Build a full schema api field definition. * * @param $stub * Array with at least one key 'type'. */ -function data_build_field_spec($stub) { +function data_build_field_definition($stub) { $spec = array(); $spec['type'] = $stub['type']; if ($spec['type'] == 'int') { diff --git a/data_ui/data_ui.admin.inc b/data_ui/data_ui.admin.inc index 12f2fddf608f88ff5e229d754035694f8aab7c6f..daa5ba6fbd0cc19955202ae15022fd9b07de4ed2 100644 --- a/data_ui/data_ui.admin.inc +++ b/data_ui/data_ui.admin.inc @@ -105,30 +105,38 @@ function data_ui_add_form_validate($form, &$form_state) { * Submit handler for add table form. */ function data_ui_add_form_submit($form, &$form_state) { + if (isset($form_state['values']['field_num'])) { $form_state['storage'] = $form_state['values']; } elseif (isset($form_state['values']['fields'])) { // Create a schema from user input. - $schema = $index = $primary = array(); + $schema = $index = $primary = $meta = array(); foreach ($form_state['values']['fields'] as $field) { - $schema['fields'][$field['name']] = data_build_field_spec($field); + $schema['fields'][$field['name']] = data_build_field_definition($field); + $meta['fields'][$field['name']]['label'] = $field['label']; + + // Limit index if field type is text. if (!empty($field['index'])) { - $index[$field['name']] = array($field['name']); + $index[$field['name']] = data_get_index_definition($field['name'], $field['type']); } if (!empty($field['primary'])) { - $primary[] = $field['name']; + $primary[] = data_get_pk_definition($field['name'], $field['type']); } } $schema['indexes'] = $index; $schema['primary key'] = $primary; + + // Create table. if ($table = data_create_table(data_name(trim($form_state['storage']['name'])), $schema, trim($form_state['storage']['title']))) { + $meta = $table->update(array('meta' => $meta)); drupal_set_message(t('Created table !table', array('!table' => $table->get('name')))); } else { drupal_set_message(t('Error creating table'), 'error'); } + // Unset storage to enable redirect. unset($form_state['storage']); $form_state['redirect'] = 'admin/content/data'; @@ -191,7 +199,7 @@ function data_ui_edit_form(&$form_state, $table) { ); $form['fields'][$field_name]['type'] = array( '#type' => 'select', - '#options' => drupal_map_assoc(array('int', 'varchar', 'text')), + '#options' => data_get_field_types(), '#default_value' => $field['type'], ); $form['fields'][$field_name]['unsigned'] = array( @@ -299,7 +307,7 @@ function data_ui_edit_form_submit($form, &$form_state) { } elseif ($form_state['clicked_button']['#value'] == t('Add new')) { $new = $form_state['values']['new']; - $spec = data_build_field_spec($new); + $spec = data_build_field_definition($new); $table->addField($new['name'], $spec); if (!empty($new['index'])) { $table->addIndex($new['name']); @@ -406,7 +414,7 @@ function _data_ui_field_form($required = FALSE) { ); $form['type'] = array( '#type' => 'select', - '#options' => drupal_map_assoc(array('int', 'varchar', 'text')), + '#options' => data_get_field_types(), ); $form['unsigned'] = array( '#type' => 'checkbox', diff --git a/data_ui/data_ui.module b/data_ui/data_ui.module index 2c7b7598785eb7a3995935cc67553f4fa61c8cc1..32e30619c6578eddac17a0268af123072884f910 100644 --- a/data_ui/data_ui.module +++ b/data_ui/data_ui.module @@ -73,7 +73,6 @@ function data_ui_theme() { ); } - /** * Implementation of hook_perm(). */ diff --git a/data_ui/tests/data_ui.test b/data_ui/tests/data_ui.test index d4f0ef0e074ff3650df6554afb2ecc257fb713e8..324c689c8009dde6af2b50fb48f5c6d487fe15f1 100644 --- a/data_ui/tests/data_ui.test +++ b/data_ui/tests/data_ui.test @@ -28,11 +28,159 @@ class DataTestCaseUI extends DataTestCase { */ public function setUp() { parent::setUp('data', 'data_ui'); + + $this->drupalLogin( + $this->drupalCreateUser( + array( + 'administer data tables', + ) + ) + ); + } + + /** + * CRUD table tests on UI. + */ + public function testCRUDTable() { + $table_name = $this->createTable(5); + // @todo: edit table. + $this->dropTable($table_name); + } + + /** + * Create a table. + * + * @todo: verify schema in DB. + */ + protected function createTable($num_fields = 5) { + $table_name = $this->randomName(); + $edit = array( + 'name' => $table_name, + 'title' => 'My table', + 'field_num' => $num_fields + ); + $this->drupalPost('admin/content/data/create', $edit, 'Next'); + $this->assertText('Define the fields of the new table.'); + + $fields = $this->randomFields($num_fields); + $edit = $this->formatEditFields($fields); + $this->drupalPost(NULL, $edit, 'Create'); + // Data UI has prefixed the table name. + $table_name = data_name($table_name); + $this->assertText('Created table '. $table_name); + + // Test schema in DB. + // @todo: why do we need to clear the cache here? + if ($schema = drupal_get_schema($table_name, true)) { + foreach ($schema['primary key'] as $key) { + if (is_array($key)) { + $primary_keys[] = $key[0]; + } + else { + $primary_keys[] = $key; + } + } + foreach ($schema['fields'] as $field_name => $field) { + $this->assertEqual($fields[$field_name]['type'], $field['type'], "Field $field_name has correct type."); + if ($field['type'] == 'int') { + $this->assertEqual(isset($fields[$field_name]['unsigned']), !empty($field['unsigned']) , "Field $field_name has correct unsigned value."); + } + } + foreach ($fields as $field_name => $config) { + if (isset($config['index'])) { + $this->assertTrue(isset($schema['indexes'][$field_name]), "Field $field_name indexed."); + } + if (isset($config['primary'])) { + $this->assertTrue(in_array($field_name, $primary_keys), "Field $field_name in primary key."); + } + } + } + else { + $this->assertTrue(FALSE, 'Could not create schema.'); + } + + return $table_name; + } + + /** + * Drop a table. + */ + protected function dropTable($table_name) { + $this->drupalPost('admin/content/data/'. $table_name .'/drop', array(), 'Drop'); + $exists = db_result(db_query('SELECT name FROM {data_tables} WHERE name = "%s"', $table_name)); + $this->assertFalse($exists, 'Table removed from data_tables table.'); + $this->assertFalse(drupal_get_schema($table_name, true), 'Table '. $table_name .' removed from schema API.'); + $this->assertFalse(db_table_exists($table_name), 'Table '. $table_name .' removed from DB.'); + } + + /** + * Format an edit array from the result of randomFields(). + */ + protected function formatEditFields($fields) { + $edit = array(); + $fields = array_values($fields); + foreach ($fields as $i => $field) { + foreach ($field as $k => $v) { + $edit["fields[field_$i][$k]"] = $v; + } + } + return $edit; + } + + /** + * Generate N random fields. Will create at least 1 field. + */ + protected function randomFields($n = 5) { + $fields = array(); + for ($i = 0; $i < $n-1; $i++) { + $label = $this->uniqueRandomName(); + $name = data_safe_name($label); + $fields[$name] = array( + 'name' => $name, + 'label' => $label, + 'type' => $this->randomValue(data_get_field_types()), + ); + if (rand(0, 1)) { + $fields[$name]['unsigned'] = 1; + } + if (rand(0, 1)) { + $fields[$name]['index'] = 1; + } + if (rand(0, 1)) { + $fields[$name]['primary'] = 1; + } + } + // Make sure we have at least one field that is text, PK and indexed. + $name = $this->uniqueRandomName(); + $fields[data_safe_name($name)] = array( + 'name' => data_safe_name($name), + 'label' => $name, + 'type' => 'text', + 'index' => 1, + 'primary' => 1, + ); + + return $fields; + } + + /** + * Get a random value from the given array. + */ + protected function randomValue($array) { + $array = array_values($array); + return $array[rand(0, count($array) - 1)]; } /** - * Run admin UI tests. + * Create a _unique_ random name. */ - public function testAdminUI() { + protected function uniqueRandomName() { + static $names; + do { + $name = $this->randomName(); + } + while (isset($names[$name])); + $names[$name] = $name; + return $name; } } \ No newline at end of file