/[drupal]/contributions/modules/dataapi/dataapi.module
ViewVC logotype

Contents of /contributions/modules/dataapi/dataapi.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Tue May 1 17:59:54 2007 UTC (2 years, 6 months ago) by nedjo
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-php
A roughed in idea of how we might produce a schema-aware Data API for Drupal core. This is not intended to be a module for use on sites, but rather a place to work out some ideas and approaches for improving object handling methods in Drupal core.
1 <?php
2 // $Id: $
3
4 /**
5 * @file
6 * Schema-based API data transactions in Drupal. This is a module developed for demonstration
7 * purposes and not intended to be used on sites.
8 */
9
10 /**
11 * Generate a default data input form for a given type based on its registered
12 * fields and their references.
13 *
14 * This form is only the first step in generating an input form; generally it will need
15 * to be transformed via form_alter() before being presented to the user.
16 *
17 * References, e.g., the uid field in node table, are represented as appropriate as selects,
18 * radios, or autocomplete fields drawing data from the source table.
19 *
20 * @param $type
21 * The type of item.
22 * @return
23 * A form array.
24 */
25 function dataapi_get_type_form($type) {
26 $form = array();
27 if ($table = dataapi_get_table($type)) {
28 $references = dataapi_get_references($type);
29 foreach ($element_children($table) as $field) {
30
31 }
32 }
33 return $form;
34 }
35
36 /**
37 * Load an item if it exists.
38 *
39 * @param $type
40 * The type of item.
41 * @param $item
42 * Array of parameters to match. If an ID is fed in, the item is matched by ID. If an array
43 * is fed in, the item is loaded according to matching properties.
44 * @param $mode
45 * Loading mode. Values are:
46 * - DATAAPI_LOAD_PARTIAL. Load only the item's direct properties (in its primary table).
47 * - DATAAPI_LOAD_FULL (the default). Load the item and all associated data.
48 * - DATAAPI_LOAD_EXTENDED. Load the item and all associated data, and keep loading the associated
49 * records' associations. This option is potentially dangerous, as it could lead to a loop.
50 * @return
51 * Boolean representing success or failure.
52 */
53 function dataapi_load($type, &$item, $mode = DATAAPI_LOAD_FULL) {
54 if (is_numeric($item)) {
55 // Need to load an item by id.
56 if (!$item = dataapi_get_item_by_id($type, $item)) {
57 return FALSE;
58 }
59 }
60 else {
61 if (!$item = dataapi_get_item_by_match($type, $item)) {
62 return FALSE;
63 }
64 }
65 if (!empty($item)) {
66 $table = dataapi_get_table($type));
67 // Unpack (deserialize) the fields in the table as necessary.
68 foreach (element_children($table) as $field) {
69 if (isset($item[$field]) {
70 $item[$field] = dataapi_field_value_load($item[$field], $table[$field]);
71 }
72 }
73 // If we are doing a full load, load associated data as well.
74 if (in_array($mode, array(DATAAPI_LOAD_FULL, DATAAPI_LOAD_FULL_EXTENDED))) {
75 if ($references = dataapi_get_references($type) {
76 $schema = dataapi_get_schema();
77 foreach ($reference as $table => $reference) {
78 if ($reference['#type'] == DATAAPI_REFERENCE_FOREIGN_KEY) {
79 $item['#'. $table] = dataapi_load($table, $item[$reference['#referenced_key']], $mode == DATAAPI_LOAD_FULL_EXTENDED ? $mode : DATAAPI_LOAD_PARTIAL);
80 }
81 }
82 }
83 }
84 return TRUE;
85 }
86 return FALSE;
87 }
88
89 /**
90 * Load an item if it exists.
91 *
92 * @param $type
93 * The type of item.
94 * @param &$item
95 * Array of parameters to save. If an ID is fed in, the item is matched by ID. If an array
96 * is fed in, the item is loaded according to matching properties.
97 * @param $mode
98 * A constant representing the loading mode. Values are:
99 * - DATAAPI_MATCH_MODE_LOOSE. To determine if the item exists, match by registered #match_field values.
100 * - DATAAPI_MATCH_MODE_STRICT. Only match an existing item by ID.
101 * @return
102 * A constant representing the result of the operation.
103 */
104 function dataapi_save($type, &$item, $mode = DATAAPI_MATCH_MODE_STRICT) {
105 $return = FALSE;
106 if ($table = dataapi_get_table($type)) {
107 $id_field = dataapi_get_fields_by_property('#id_field', $table);
108 if (isset($item[$id_field]) {
109 // Id field is set, so this is an existing item.
110 // Ensure the item exists.
111 if ($existing = dataapi_load($type, $item[$id_field]) {
112 $return = SAVED_UPDATED;
113 // Fields of this table.
114 foreach (element_children($table) as $field) {
115 if (isset($item[$field]) {
116 $item[$field] = dataapi_field_value_save($item[$field], $table[$field]);
117 }
118
119 }
120 }
121 else {
122 drupal_set_message('system', t('Attempt to save %type failed: ID %id doesn\'t exist.', array('%type' => $type, '%id' => $item[$id_field])), 'error');
123 }
124 }
125 elseif ($mode == DATAAPI_MATCH_MODE_LOOSE && $existing = dataapi_load($type, $item)) {
126 $return = SAVED_UPDATED;
127 }
128 else {
129 $return = SAVED_NEW;
130 // Ensure required fields are set.
131 $required = dataapi_get_fields_by_property('#required', $table, TRUE);
132
133 // Since this is a new item, give modules the ability to set intelligent defaults.
134 // This might be, e.g., the default settings for a node depending on type.
135 module_invoke_all_by_reference('defaults', $type, $item);
136
137 // Set the ID field if required.
138 if (isset($table[$id_field]['#id_increment']) && $table[$id_field]['#id_increment'] == DATAAPI_DB_ID_INCREMENT_CUSTOM) {
139 $item[$id_field] = db_next_id('{'. $type .'}_'. $id_field);
140 }
141 // Save item.
142
143
144 }
145 // Data belonging in other tables are identified by a key in the form #table_name.
146 foreach (element_properties($table) as $external_table) {
147
148 }
149 if ($return !== FALSE) {
150 module_invoke_all_by_reference('save', $type, $item);
151 }
152 }
153
154 return $return;
155 }
156
157 /**
158 * Test an input array for missing keys.
159 *
160 * @param $array
161 * Array to evaluate.
162 * @param $required_keys
163 * An array of keys to test for.
164 * @return
165 * An array of the missing keys, or FALSE if all keys are there.
166 */
167 function dataapi_missing_requirements($array, $required_keys) {
168 $intersect = array_intersect(array_keys($array), $required_keys);
169 $missing = array_diff($required_keys, $intersect);
170 return !empty($missing) ? $missing : FALSE;
171 }
172
173 /**
174 * Prepare a field value for writing to the database.
175 *
176 * This method handles serializing and encrypting data.
177 *
178 * @param $value
179 * The existing value.
180 * @param $field
181 * Array of field properties.
182 * @return
183 * The prepared value.
184 */
185 function dataapi_field_value_save($value, $field) {
186 if (isset($field['serialize']) && $field['serialize']) {
187 $value = serialize($value);
188 }
189 if (isset($field['encrypt']) && $field['encrypt']) {
190 $value = md5($value);
191 }
192 return $value;
193 }
194
195 /**
196 * Prepare a field value for reading.
197 *
198 * This method handles unserializing data.
199 *
200 * @param $value
201 * The value as loaded from the database.
202 * @param $field
203 * Array of field properties.
204 * @return
205 * The prepared value.
206 */
207 function dataapi_field_value_load($value, $field) {
208 if (isset($field['serialize']) && $field['serialize']) {
209 $value = unserialize($value);
210 }
211 // No way to return the unencrypted hash, so no handling of $field['encrypt'].
212 return $value;
213 }
214
215 /**
216 * Return the site's schema.
217 *
218 * @return
219 * a nested array representing all data types on the site.
220 */
221 function dataapi_get_schema() {
222 static $schema;
223 if ($schema == NULL) {
224 $schema = array(
225 '#rdbms' => $GLOBALS['db_type'],
226 );
227 $schema += module_invoke_all('tables');
228 }
229 return $schema;
230 }
231
232 /**
233 * Return the properties of a table.
234 *
235 * @param $name
236 * The table name.
237 * @return
238 * The table if found, otherwise FALSE.
239 */
240 function dataapi_get_table($name) {
241 $schema = dataapi_get_schema();
242 return isset($schema[$name]) ? $schema[$name] : FALSE;
243 }
244
245 /**
246 * Load an item if it exists.
247 *
248 * @param $property
249 * The property of item.
250 * @param $table
251 * Array of table properties, with nested fields.
252 * @param $multiple
253 * Boolean, are multiple fields to be returned
254 * @return
255 * An array of fields if found, otherwise FALSE.
256 */
257 function dataapi_get_fields_by_property($property, $table, $multiple = FALSE) {
258 if ($multiple) {
259 $return = array();
260 }
261 foreach (element_children($table) as $field) {
262 if (isset($table[$field][$property]) && $table[$field][$property]) {
263 if (!$multiple) {
264 return $field;
265 }
266 else {
267 $return[] = $field;
268 }
269 }
270 }
271 return !empty($return) ? $return : FALSE;
272 }
273
274 /**
275 * Return an item matching an ID.
276 *
277 * @param $type
278 * The type of item.
279 * @param $id
280 * The ID to look for.
281 * @return
282 * An existing item if found, otherwise FALSE.
283 */
284 function dataapi_get_item_by_id($type, $id) {
285 if ($table = dataapi_get_table($type)) {
286 $id_field = dataapi_get_fields_by_property('#id_field', $table);
287 $sql = 'SELECT * FROM {'. $type .'} WHERE '. $id_field .' = %d';
288 $result = db_query($sql, $id);
289 return db_num_rows($result) ? db_fetch_array($result) : FALSE;
290 }
291 return FALSE;
292 }
293
294 /**
295 * Return an item matching given parameters.
296 *
297 * @param $type
298 * The type of item.
299 * @param $item
300 * Array of parameters to match.
301 * @return
302 * An existing item if found, otherwise FALSE.
303 */
304 function dataapi_get_item_by_match($type, $item) {
305 if ($table = dataapi_get_table($type)) {
306 $matches = dataapi_get_fields_by_property('#match_field', $table, TRUE);
307 $where = array();
308 $params = array();
309 foreach ($matches as $field) {
310 if (isset($item[$field]) {
311 $where[] = $field .'='. db_escape_encode($table[$field]['data_type']);
312 $params[] = $item[$field];
313 }
314 }
315 if (!empty($where)) {
316 $sql = 'SELECT * FROM {'. $type .'} WHERE '. implode($table['#match_behavior'] == DATAAPI_MATCH_BEHAVIOR_ALL ? ' AND ' : ' OR ', $where);
317 $result = db_query($sql, $params);
318 return db_num_rows($result) ? db_fetch_array($result) : FALSE;
319 }
320 }
321 return FALSE;
322 }
323
324 /**
325 * Return the appropriate escape encoding for a data type.
326 *
327 * @param $data_type
328 * A data type.
329 * @return
330 * An escape encoding.
331 */
332 function db_escape_encode($data_type) {
333 switch ($data_type) {
334 case 'date':
335 case 'tinyint':
336 case 'int':
337 return '%d';
338 case 'varchar':
339 case 'text':
340 return '"%s"';
341 }
342 }
343
344 /**
345 * Test an input array for missing keys.
346 *
347 * @param $item
348 * Item to evaluate.
349 * @param $required_keys
350 * An array of keys to test for.
351 * @return
352 * An array of the missing keys, or FALSE if all keys are there.
353 */
354 function datapai_missing_requirements($item, $required_keys) {
355 $intersect = array_intersect(array_keys($item), $required_keys);
356 $missing = array_diff($required_keys, $intersect);
357 return !empty($missing) ? $missing : FALSE;
358 }
359
360 /**
361 * Return an array of references in a given table.
362 *
363 * @param $type
364 * Data type (table).
365 * @return
366 * An array references.
367 */
368 function dataapi_get_references($type) {
369 $references = array();
370 $schema = dataapi_get_schema();
371 foreach (element_children($schema) as $table) {
372 foreach (element_children($schema[$table]) as $field) {
373 if (isset($schema[$table][$field]['#reference']) && $schema[$table][$field]['#reference']['#table'] == $type) {
374 $references[$schema[$table][$field]['#table']] = array(
375 '#type' => $schema[$table][$field]['#reference']['#type'],
376 '#referring_key' => $field,
377 '#referenced_key' => $schema[$table][$field]['#reference']['#type'] == DATAAPI_REFERENCE_FOREIGN_KEY ? dataapi_get_field_type($schema[$type], 'id') : $schema[$table][$field]['#field'],
378 );
379 }
380 }
381 }
382 return !empty($references) ? $references : FALSE;
383 }
384
385 /**
386 * Return the standard options for a boolean togglable setting.
387 *
388 * @return
389 * An array options.
390 */
391 function _datapi_enabled_options() {
392 return array(DATAAPI_OPTION_DISABLED => t('Disabled'), DATAAPI_OPTION_ENABLED => t('Enabled'));
393 }
394
395 /**
396 * Invoke a hook in all enabled modules that implement it, passing the second argument
397 * by reference.
398 *
399 * @param $hook
400 * The name of the hook to invoke.
401 * @param $type
402 * The name of the hook to invoke.
403 * @return
404 * An array of return values of the hook implementations. If modules return
405 * arrays from their implementations, those are merged into one array.
406 */
407 function module_invoke_all_by_reference($hook, $type, &$item) {
408 $return = array();
409 foreach (module_implements($hook) as $module) {
410 $function = $module .'_'. $hook;
411 $result = $function($type, $item);
412 if (isset($result) && is_array($result)) {
413 $return = array_merge($return, $result);
414 }
415 else if (isset($result)) {
416 $return[] = $result;
417 }
418 }
419
420 return $return;
421 }

  ViewVC Help
Powered by ViewVC 1.1.2