| Commit | Line | Data |
|---|---|---|
| 2ace3f91 AB |
1 | <?php |
| 2 | // $Id$ | |
| 3a16de16 AB |
3 | /** |
| 4 | * @file | |
| 5 | * Hooks and API functions for data module. | |
| 6 | */ | |
| 2ace3f91 | 7 | |
| 2ace3f91 | 8 | /** |
| c0bdd519 AB |
9 | * Implementation of hook_views_api(). |
| 10 | */ | |
| 11 | function data_views_api() { | |
| 12 | return array( | |
| 13 | 'api' => '2.0', | |
| cff26223 | 14 | 'path' => drupal_get_path('module', 'data'), |
| 34583f68 | 15 | ); |
| c0bdd519 AB |
16 | } |
| 17 | ||
| 18 | /** | |
| f48c6947 | 19 | * Create a table. |
| 825e0da0 AB |
20 | * |
| 21 | * Usage: | |
| f48c6947 | 22 | * $table = data_create_table('my_table', $schema, 'My table'); |
| 825e0da0 | 23 | * $table->save($data); |
| 2ace3f91 | 24 | * |
| 5a6fc132 AB |
25 | * @see DataTable class. |
| 26 | * | |
| 2ace3f91 AB |
27 | * @param $name |
| 28 | * String that identifies the data table. It is recommended to use | |
| 29 | * data_name() to generate a table name in the data namespace. For | |
| 30 | * example: $table = data_get_tabe(data_name('my_table')); | |
| f48c6947 AB |
31 | * @param $schema |
| 32 | * Schema for the table. | |
| 825e0da0 AB |
33 | * @param $title |
| 34 | * A natural title for the table. | |
| 2ace3f91 | 35 | * |
| 825e0da0 AB |
36 | * @return |
| 37 | * A DataTable object if init could create one, | |
| 38 | * FALSE if not. | |
| 2ace3f91 | 39 | */ |
| f48c6947 AB |
40 | function data_create_table($name, $schema, $title = NULL) { |
| 41 | if (_data_get_table($name)) { | |
| 825e0da0 | 42 | return FALSE; |
| 2ace3f91 | 43 | } |
| f48c6947 | 44 | return _data_get_table($name, $schema, $title); |
| 2ace3f91 AB |
45 | } |
| 46 | ||
| 47 | /** | |
| 825e0da0 | 48 | * Get a table if it exists. |
| 5a6fc132 AB |
49 | * |
| 50 | * @see DataTable class. | |
| 51 | * | |
| 825e0da0 AB |
52 | * @param $name |
| 53 | * Unique name of the table. | |
| 2ace3f91 | 54 | * |
| 825e0da0 AB |
55 | * @return |
| 56 | * A DataTable object if there is a table with this name, | |
| 57 | * FALSE if not. | |
| 2ace3f91 | 58 | */ |
| 825e0da0 | 59 | function data_get_table($name) { |
| f48c6947 | 60 | if ($table = _data_get_table($name)) { |
| 825e0da0 AB |
61 | return $table; |
| 62 | } | |
| 63 | return FALSE; | |
| 2ace3f91 AB |
64 | } |
| 65 | ||
| 66 | /** | |
| f48c6947 AB |
67 | * Drop a table - use this instead of $table->drop(). |
| 68 | */ | |
| 69 | function data_drop_table($name) { | |
| 70 | if ($table = data_get_table($name)) { | |
| 71 | $table->drop(); | |
| 72 | _data_get_table($name, NULL, NULL, TRUE); | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | /** | |
| 2ace3f91 AB |
77 | * Get schema info for all data allocated tables. |
| 78 | */ | |
| 79 | function data_get_schema() { | |
| 80 | $schema = array(); | |
| 81 | $tables = data_get_all_tables(); | |
| 82 | foreach ($tables as $table) { | |
| 83 | $schema[$table->get('name')] = $table->get('table_schema'); | |
| 84 | } | |
| 85 | return $schema; | |
| 86 | } | |
| 87 | ||
| 88 | /** | |
| 89 | * Load all data tables. | |
| 90 | */ | |
| 91 | function data_get_all_tables() { | |
| 92 | $tables = array(); | |
| 93 | $result = db_query('SELECT name FROM {data_tables}'); | |
| 9933042e AB |
94 | while ($row = db_fetch_object($result)) { |
| 95 | if ($table = data_get_table($row->name)) { | |
| 96 | $tables[$row->name] = $table; | |
| 97 | } | |
| 2ace3f91 | 98 | } |
| f48c6947 | 99 | |
| 2ace3f91 AB |
100 | return $tables; |
| 101 | } | |
| 102 | ||
| 103 | /** | |
| 19aba4f3 | 104 | * Get a list of supported field definitions. |
| 2ace3f91 AB |
105 | * |
| 106 | * This list is a sub set of Schema API data types | |
| 107 | * http://drupal.org/node/159605 | |
| 108 | * The keys are simplified handles. | |
| 2ace3f91 | 109 | */ |
| 19aba4f3 | 110 | function data_get_field_definitions() { |
| 2ace3f91 AB |
111 | return array( |
| 112 | 'int' => array( | |
| 113 | 'type' => 'int', | |
| 114 | 'not null' => FALSE, | |
| 115 | ), | |
| 116 | 'unsigned int' => array( | |
| 117 | 'type' => 'int', | |
| 118 | 'unsigned' => TRUE, | |
| 119 | 'not null' => FALSE, | |
| 120 | ), | |
| 121 | 'varchar' => array( | |
| 122 | 'type' => 'varchar', | |
| 123 | 'length' => 255, | |
| 124 | 'not null' => FALSE, | |
| 125 | ), | |
| 126 | 'text' => array( | |
| 127 | 'type' => 'text', | |
| 128 | 'not null' => FALSE, | |
| 129 | ), | |
| 130 | ); | |
| 131 | } | |
| 132 | ||
| 133 | /** | |
| 19aba4f3 | 134 | * Get a definition key into a schema API type definition. |
| 2ace3f91 | 135 | * |
| 38e93719 | 136 | * If no type can be found, FALSE will be returned. |
| 2ace3f91 | 137 | */ |
| 19aba4f3 AB |
138 | function data_get_field_definition($key) { |
| 139 | $definitions = data_get_field_definitions(); | |
| 140 | if (isset($definitions[$key])) { | |
| 141 | return $definitions[$key]; | |
| 2ace3f91 | 142 | } |
| 38e93719 | 143 | return FALSE; |
| 2ace3f91 AB |
144 | } |
| 145 | ||
| 146 | /** | |
| 19aba4f3 AB |
147 | * Get schema API field types supported by Data module. |
| 148 | */ | |
| 149 | function data_get_field_types() { | |
| 150 | $definitions = data_get_field_definitions(); | |
| 151 | $types = array(); | |
| 152 | foreach ($definitions as $def) { | |
| 153 | $types[$def['type']] = $def['type']; | |
| 154 | } | |
| 155 | return $types; | |
| 156 | } | |
| 157 | ||
| 158 | /** | |
| 159 | * Get a Schema API PK definition for a given field type. | |
| 160 | */ | |
| 161 | function data_get_pk_definition($name, $type) { | |
| 162 | if ($type == 'text') { | |
| 163 | return array($name, 255); | |
| 164 | } | |
| 165 | else { | |
| 166 | return $name; | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | /** | |
| 171 | * Get a Schema API index definition for a given field type. | |
| 172 | * @todo: support multiple name/type combinations. | |
| 173 | */ | |
| 174 | function data_get_index_definition($name, $type) { | |
| 175 | if ($type == 'text') { | |
| 176 | return array(array($name, 255)); | |
| 177 | } | |
| 178 | else { | |
| 179 | return array($name); | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | /** | |
| 2ace3f91 AB |
184 | * Create a table name in the data namespace. |
| 185 | * @todo: make overridable. | |
| 186 | */ | |
| 187 | function data_name($table) { | |
| 188 | return 'data_table_'. $table; | |
| 189 | } | |
| 190 | ||
| 191 | /** | |
| 38e93719 AB |
192 | * Create a safe name for MySQL field or table names. |
| 193 | * | |
| 194 | * @todo: IMPROVE. | |
| 195 | * | |
| 196 | * - make sure all unsafe characters are removed. | |
| 197 | * - filter magic words. | |
| 198 | * - test pgsql. | |
| 2ace3f91 AB |
199 | */ |
| 200 | function data_safe_name($name) { | |
| 201 | $map = array( | |
| 202 | '.' => '_', | |
| 203 | ':' => '', | |
| 204 | '/' => '', | |
| 205 | '-' => '_', | |
| 206 | ' ' => '_', | |
| 207 | ',' => '_', | |
| 208 | ); | |
| 209 | $simple = trim(strtolower(strip_tags($name))); | |
| 210 | // Limit length to 64 as per http://dev.mysql.com/doc/refman/5.0/en/identifiers.html | |
| 211 | $simple = substr(strtr($simple, $map), 0, 64); | |
| 212 | ||
| 213 | if (is_numeric($simple)) { | |
| 214 | // We need to escape numerics because Drupal's drupal_write_record() | |
| 215 | // does not properly escape token MYSQL names. | |
| 216 | $simple = '__num_'. $simple; | |
| 217 | } | |
| 218 | return db_escape_table($simple); | |
| 219 | } | |
| 220 | ||
| 221 | /** | |
| 222 | * Helper function to create a natural name. | |
| 223 | * underscored_name -> Underscored name | |
| 224 | */ | |
| 225 | function data_natural_name($name) { | |
| 226 | return ucfirst(strtolower(str_replace('_', ' ', $name))); | |
| 227 | } | |
| 228 | ||
| 229 | /** | |
| 2ace3f91 AB |
230 | * Helper function to generate a schema. |
| 231 | * | |
| 232 | * Example: | |
| 233 | * $table->create(data_build_schema($keys)); | |
| 234 | * | |
| 235 | * @todo: check for table name collisions | |
| 236 | * @todo: add type detection | |
| 237 | * @todo: add meta info handling | |
| 238 | * @todo: add primary key handling | |
| 239 | * @todo: may be add option to add a full fledged schema here? | |
| 240 | */ | |
| 241 | function data_build_schema($keys) { | |
| 38e93719 AB |
242 | // Build the table definition. |
| 243 | // Fall back to varchar if no valid type is given. | |
| 19aba4f3 AB |
244 | $fields = $schema = array(); |
| 245 | foreach ($keys as $k => $key) { | |
| 246 | if ($definition = data_get_field_definition($key)) { | |
| 247 | $fields[data_safe_name($k)] = $definition; | |
| 38e93719 AB |
248 | } |
| 249 | else { | |
| 19aba4f3 | 250 | $fields[data_safe_name($k)] = data_get_field_definition('varchar'); |
| 38e93719 | 251 | } |
| 2ace3f91 | 252 | } |
| 38e93719 | 253 | |
| 2ace3f91 AB |
254 | $schema['fields'] = $fields; |
| 255 | $schema['indexes'] = array(); | |
| 256 | return $schema; | |
| 257 | } | |
| 258 | ||
| 259 | /** | |
| 19aba4f3 | 260 | * Build a full schema api field definition. |
| 2ace3f91 AB |
261 | * |
| 262 | * @param $stub | |
| 263 | * Array with at least one key 'type'. | |
| 264 | */ | |
| 19aba4f3 | 265 | function data_build_field_definition($stub) { |
| 2ace3f91 AB |
266 | $spec = array(); |
| 267 | $spec['type'] = $stub['type']; | |
| 268 | if ($spec['type'] == 'int') { | |
| 269 | $spec['unsigned'] = empty($stub['unsigned']) ? FALSE : TRUE; | |
| 270 | } | |
| 271 | if ($spec['type'] == 'varchar') { | |
| 272 | $spec['length'] = 255; | |
| 273 | } | |
| 274 | return $spec; | |
| 275 | } | |
| 276 | ||
| 277 | /** | |
| 83dadde7 AB |
278 | * Internal singleton/factory function for creating a single instance of a DataTable class. |
| 279 | * | |
| 280 | * Don't use this function directly. Call data_create_table() or data_get_table() instead. | |
| 281 | * | |
| 282 | * If a schema is given, _data_get_table() creates the table objects DB structure. | |
| 283 | * | |
| 284 | * The purpose of this function is to make sure that | |
| 285 | * | |
| 286 | * a) there is only a single DataTable object for accessing a specific DataTable. | |
| 287 | * b) there is no DataTable object that does not have an existing table. | |
| 288 | */ | |
| 289 | function _data_get_table($name, $schema = NULL, $title = NULL, $reset = FALSE) { | |
| 290 | _data_include(); | |
| 291 | ||
| 292 | static $tables; | |
| 293 | // Simple way of having a way to override the class being used. | |
| 294 | // This could be refined with a $type parameter in _data_get_table() and depending | |
| 295 | // functions. | |
| 296 | $class = variable_get('data_table_class', 'DataTable'); | |
| 297 | if ($reset) { | |
| 298 | unset($tables[$name]); | |
| 299 | } | |
| 300 | ||
| 301 | if (!isset($tables[$name])) { | |
| 302 | if (db_result(db_query('SELECT name FROM {data_tables} WHERE name = "%s"', $name))) { | |
| 303 | $tables[$name] = new $class($name); | |
| 304 | } | |
| 305 | } | |
| 306 | if ($schema) { | |
| 307 | $tables[$name] = new $class($name, $schema, $title); | |
| 308 | } | |
| 309 | return isset($tables[$name]) ? $tables[$name] : FALSE; | |
| 310 | } | |
| 311 | ||
| 312 | /** | |
| d826902e | 313 | * Include class file. |
| 2ace3f91 | 314 | */ |
| d826902e AB |
315 | function _data_include() { |
| 316 | static $included; | |
| 317 | if (!$included) { | |
| 318 | include drupal_get_path('module', 'data') .'/data.inc'; | |
| 825e0da0 | 319 | } |
| d826902e AB |
320 | $included = TRUE; |
| 321 | } |