Remove adjust() - deprecated.
[project/data.git] / data.module
CommitLineData
2ace3f91
AB
1<?php
2// $Id$
3a16de16
AB
3/**
4 * @file
5 * Hooks and API functions for data module.
12aae5b6 6 * @todo: Move helper functions into data.inc or other .inc file.
3a16de16 7 */
2ace3f91 8
72ba49d9
AB
9// Constant designating an undefined export state.
10// Used in absence of EXPORT_IN_CODE, EXPORT_IN_DATABASE
11define('DATA_EXPORT_UNDEFINED', 0);
12
2ace3f91 13/**
c0bdd519
AB
14 * Implementation of hook_views_api().
15 */
16function data_views_api() {
17 return array(
18 'api' => '2.0',
cff26223 19 'path' => drupal_get_path('module', 'data'),
34583f68 20 );
c0bdd519
AB
21}
22
23/**
72ba49d9
AB
24 * Implementation of hook_features_api().
25 */
26function data_features_api() {
27 return array(
28 'default_hook' => 'data_default',
29 'file' => drupal_get_path('module', 'data') .'/data.features.inc',
30 );
31}
32
33/**
f48c6947 34 * Create a table.
825e0da0
AB
35 *
36 * Usage:
f48c6947 37 * $table = data_create_table('my_table', $schema, 'My table');
825e0da0 38 * $table->save($data);
2ace3f91 39 *
5a6fc132
AB
40 * @see DataTable class.
41 *
2ace3f91
AB
42 * @param $name
43 * String that identifies the data table. It is recommended to use
44 * data_name() to generate a table name in the data namespace. For
45 * example: $table = data_get_tabe(data_name('my_table'));
f48c6947
AB
46 * @param $schema
47 * Schema for the table.
825e0da0
AB
48 * @param $title
49 * A natural title for the table.
2ace3f91 50 *
825e0da0
AB
51 * @return
52 * A DataTable object if init could create one,
53 * FALSE if not.
2ace3f91 54 */
f48c6947 55function data_create_table($name, $schema, $title = NULL) {
72ba49d9 56 if (_data_get_table($name, NULL, NULL, TRUE)) {
825e0da0 57 return FALSE;
2ace3f91 58 }
f48c6947 59 return _data_get_table($name, $schema, $title);
2ace3f91
AB
60}
61
62/**
825e0da0 63 * Get a table if it exists.
5a6fc132
AB
64 *
65 * @see DataTable class.
66 *
825e0da0
AB
67 * @param $name
68 * Unique name of the table.
2ace3f91 69 *
825e0da0
AB
70 * @return
71 * A DataTable object if there is a table with this name,
72 * FALSE if not.
2ace3f91 73 */
825e0da0 74function data_get_table($name) {
f48c6947 75 if ($table = _data_get_table($name)) {
825e0da0
AB
76 return $table;
77 }
78 return FALSE;
2ace3f91
AB
79}
80
81/**
f48c6947
AB
82 * Drop a table - use this instead of $table->drop().
83 */
84function data_drop_table($name) {
85 if ($table = data_get_table($name)) {
86 $table->drop();
87 _data_get_table($name, NULL, NULL, TRUE);
88 }
89}
90
91/**
2ace3f91 92 * Get schema info for all data allocated tables.
72ba49d9
AB
93 *
94 * Pull directly from database to avoid race conditions.
2ace3f91
AB
95 */
96function data_get_schema() {
97 $schema = array();
72ba49d9
AB
98 $result = db_query('SELECT name, table_schema FROM {data_tables}');
99 while ($table = db_fetch_object($result)) {
100 $schema[$table->name] = unserialize($table->table_schema);
2ace3f91
AB
101 }
102 return $schema;
103}
104
105/**
106 * Load all data tables.
107 */
108function data_get_all_tables() {
109 $tables = array();
72ba49d9
AB
110 if ($tables = _data_load_table()) {
111 foreach ($tables as $table_name => $table) {
112 if ($table = data_get_table($table_name)) {
113 $tables[$table_name] = $table;
114 }
9933042e 115 }
2ace3f91
AB
116 }
117 return $tables;
118}
119
120/**
19aba4f3 121 * Get a list of supported field definitions.
2ace3f91
AB
122 *
123 * This list is a sub set of Schema API data types
124 * http://drupal.org/node/159605
125 * The keys are simplified handles.
2ace3f91 126 */
19aba4f3 127function data_get_field_definitions() {
2ace3f91
AB
128 return array(
129 'int' => array(
130 'type' => 'int',
131 'not null' => FALSE,
132 ),
133 'unsigned int' => array(
134 'type' => 'int',
135 'unsigned' => TRUE,
136 'not null' => FALSE,
137 ),
138 'varchar' => array(
139 'type' => 'varchar',
140 'length' => 255,
141 'not null' => FALSE,
142 ),
143 'text' => array(
144 'type' => 'text',
145 'not null' => FALSE,
146 ),
147 );
148}
149
150/**
19aba4f3 151 * Get a definition key into a schema API type definition.
2ace3f91 152 *
38e93719 153 * If no type can be found, FALSE will be returned.
2ace3f91 154 */
19aba4f3
AB
155function data_get_field_definition($key) {
156 $definitions = data_get_field_definitions();
157 if (isset($definitions[$key])) {
158 return $definitions[$key];
2ace3f91 159 }
38e93719 160 return FALSE;
2ace3f91
AB
161}
162
163/**
19aba4f3
AB
164 * Get schema API field types supported by Data module.
165 */
166function data_get_field_types() {
167 $definitions = data_get_field_definitions();
168 $types = array();
169 foreach ($definitions as $def) {
170 $types[$def['type']] = $def['type'];
171 }
172 return $types;
173}
174
175/**
176 * Get a Schema API PK definition for a given field type.
177 */
178function data_get_pk_definition($name, $type) {
179 if ($type == 'text') {
180 return array($name, 255);
181 }
182 else {
183 return $name;
184 }
185}
186
187/**
188 * Get a Schema API index definition for a given field type.
189 * @todo: support multiple name/type combinations.
190 */
191function data_get_index_definition($name, $type) {
192 if ($type == 'text') {
193 return array(array($name, 255));
194 }
195 else {
196 return array($name);
197 }
198}
199
200/**
2ace3f91
AB
201 * Create a table name in the data namespace.
202 * @todo: make overridable.
203 */
204function data_name($table) {
205 return 'data_table_'. $table;
206}
207
208/**
38e93719
AB
209 * Create a safe name for MySQL field or table names.
210 *
211 * @todo: IMPROVE.
212 *
213 * - make sure all unsafe characters are removed.
214 * - filter magic words.
215 * - test pgsql.
2ace3f91
AB
216 */
217function data_safe_name($name) {
218 $map = array(
219 '.' => '_',
220 ':' => '',
221 '/' => '',
222 '-' => '_',
223 ' ' => '_',
224 ',' => '_',
225 );
226 $simple = trim(strtolower(strip_tags($name)));
227 // Limit length to 64 as per http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
228 $simple = substr(strtr($simple, $map), 0, 64);
229
230 if (is_numeric($simple)) {
231 // We need to escape numerics because Drupal's drupal_write_record()
232 // does not properly escape token MYSQL names.
233 $simple = '__num_'. $simple;
234 }
235 return db_escape_table($simple);
236}
237
238/**
239 * Helper function to create a natural name.
240 * underscored_name -> Underscored name
241 */
242function data_natural_name($name) {
243 return ucfirst(strtolower(str_replace('_', ' ', $name)));
244}
245
246/**
2ace3f91
AB
247 * Helper function to generate a schema.
248 *
249 * Example:
250 * $table->create(data_build_schema($keys));
251 *
252 * @todo: check for table name collisions
253 * @todo: add type detection
254 * @todo: add meta info handling
255 * @todo: add primary key handling
256 * @todo: may be add option to add a full fledged schema here?
257 */
258function data_build_schema($keys) {
38e93719
AB
259 // Build the table definition.
260 // Fall back to varchar if no valid type is given.
19aba4f3
AB
261 $fields = $schema = array();
262 foreach ($keys as $k => $key) {
263 if ($definition = data_get_field_definition($key)) {
264 $fields[data_safe_name($k)] = $definition;
38e93719
AB
265 }
266 else {
19aba4f3 267 $fields[data_safe_name($k)] = data_get_field_definition('varchar');
38e93719 268 }
2ace3f91 269 }
38e93719 270
2ace3f91
AB
271 $schema['fields'] = $fields;
272 $schema['indexes'] = array();
273 return $schema;
274}
275
276/**
19aba4f3 277 * Build a full schema api field definition.
2ace3f91
AB
278 *
279 * @param $stub
280 * Array with at least one key 'type'.
281 */
19aba4f3 282function data_build_field_definition($stub) {
2ace3f91
AB
283 $spec = array();
284 $spec['type'] = $stub['type'];
285 if ($spec['type'] == 'int') {
286 $spec['unsigned'] = empty($stub['unsigned']) ? FALSE : TRUE;
287 }
288 if ($spec['type'] == 'varchar') {
289 $spec['length'] = 255;
290 }
291 return $spec;
292}
293
294/**
12aae5b6
AB
295 * Helper for building a schema API conform index array.
296 */
297function data_build_index_array($field_name, $spec) {
298 // Default to 255 for now.
299 if ($spec['type'] == 'text') {
300 return array(array($field_name, 255));
301 }
302 else {
303 return array($field_name);
304 }
305}
306
307/**
308 * Helper function for adjusting a table's real schema.
309 * @todo: this should live in schema module and should use better defined $reason keys.
310 */
311function data_alter_table($table, $field, $reason) {
312 $schema = $table->get('table_schema');
313
314 switch ($reason) {
315 case 'not in database':
316 if (isset($schema['fields'][$field])) {
317 return $table->addField($field, $schema['fields'][$field]);
318 }
319 return FALSE;
320
321 case 'missing in database':
322 list($type, $field) = explode(' ', $field);
323 // @todo: support multiple keys.
324 if ($type == 'indexes') {
325 return $table->addIndex($field);
326 }
327 elseif ($type == 'unique keys') {
328 return $table->addUniqueKey($field);
329 }
330 elseif ($type == 'primary key') {
331 return $table->addPrimaryKey($schema['primary keys']);
332 }
333 return FALSE;
334
335 case 'primary key:<br />declared': // @todo: yikes!
336 $table->dropPrimaryKey();
337 return $table->changePrimaryKey($schema['primary keys']);
338 case 'missing in schema':
339 if ($field == 'primary key') {
340 return $table->dropPrimaryKey();
341 }
342 return FALSE;
343
344 case 'unexpected column in database':
345 return $this->dropField($field);
346
347 }
348 return FALSE;
349}
350
351/**
72ba49d9
AB
352 * Export a data table. This does not export the content of a table - only its schema
353 * and any meta information (title, name, meta...).
354 *
355 * @param $name
356 * The name of the table to be exported.
357 *
358 * @return
359 * Exportable code.
360 *
361 * Only available if ctools is installed.
362 */
363function data_export($name, $indent = '') {
364 if (module_exists('ctools')) {
365 ctools_include('export');
366 $result = ctools_export_load_object('data_tables', 'names', array($name));
367 if (isset($result[$name])) {
368 return ctools_export_object('data_tables', $result[$name], $indent);
369 }
370 }
371 return t('Export requires CTools http://drupal.org/project/ctools');
372}
373
374/**
83dadde7
AB
375 * Internal singleton/factory function for creating a single instance of a DataTable class.
376 *
377 * Don't use this function directly. Call data_create_table() or data_get_table() instead.
378 *
379 * If a schema is given, _data_get_table() creates the table objects DB structure.
380 *
381 * The purpose of this function is to make sure that
382 *
383 * a) there is only a single DataTable object for accessing a specific DataTable.
384 * b) there is no DataTable object that does not have an existing table.
385 */
386function _data_get_table($name, $schema = NULL, $title = NULL, $reset = FALSE) {
387 _data_include();
388
389 static $tables;
390 // Simple way of having a way to override the class being used.
391 // This could be refined with a $type parameter in _data_get_table() and depending
392 // functions.
393 $class = variable_get('data_table_class', 'DataTable');
394 if ($reset) {
395 unset($tables[$name]);
396 }
397
398 if (!isset($tables[$name])) {
72ba49d9
AB
399 // Try whether we can load table, then instantiate. Object will then load itself.
400 if (_data_load_table($name, $reset)) {
83dadde7
AB
401 $tables[$name] = new $class($name);
402 }
403 }
404 if ($schema) {
405 $tables[$name] = new $class($name, $schema, $title);
406 }
407 return isset($tables[$name]) ? $tables[$name] : FALSE;
408}
409
410/**
72ba49d9
AB
411 * Loads data table info from the database. Uses CTools if available.
412 */
413function _data_load_table($name = NULL, $reset = FALSE) {
414 if (module_exists('ctools')) {
415 ctools_include('export');
416 if (empty($name)) {
417 return ctools_export_load_object('data_tables', 'all', array(), $reset);
418 }
419 else {
420 $tables = ctools_export_load_object('data_tables', 'names', array($name), $reset);
421 if (isset($tables[$name])) {
422 return $tables[$name];
423 }
424 return FALSE;
425 }
426 }
427 // If CTools is not available, load directly from DB.
428 if (empty($name)) {
429 $result = db_query('SELECT * FROM {data_tables}');
430 $tables = array();
431 while ($row = db_fetch_object($result)) {
432 foreach (array('table_schema', 'meta') as $key) {
433 $row->$key = unserialize($row->$key);
434 }
435 // No export type.
436 $row->export_type = DATA_EXPORT_UNDEFINED;
437 $tables[$row->name] = $row;
438 }
439 return $tables;
440 }
441 if ($table = db_fetch_object(db_query('SELECT * FROM {data_tables} WHERE name = "%s"', $name))) {
442 // No export type.
443 $table->export_type = DATA_EXPORT_UNDEFINED;
444 return $table;
445 }
446 return FALSE;
447}
448
449/**
450 * Starts overriding a data table by copying it from the default definition into the DB.
451 * This function does not have any effect if called on a table that does already exist in
452 * data_tables.
453 */
454function _data_override($name) {
455 if (!db_result(db_query('SELECT name FROM {data_tables} WHERE name = "%s"', $name))) {
456 if ($table = _data_load_table($name)) {
457 drupal_write_record('data_tables', $table);
458 }
459 }
460}
461
462/**
d826902e 463 * Include class file.
2ace3f91 464 */
d826902e
AB
465function _data_include() {
466 static $included;
467 if (!$included) {
468 include drupal_get_path('module', 'data') .'/data.inc';
825e0da0 469 }
d826902e
AB
470 $included = TRUE;
471}