- Patch #169982 by moshe, eaton, bjaspan, nedjo, yched, et al: missing feature from schema API: load/save records based upon schema.

6.x
Dries Buytaert 2007-10-02 16:15:56 +00:00
parent c389c90529
commit 4bb5080ebe
8 changed files with 184 additions and 76 deletions

View File

@ -2994,6 +2994,147 @@ function _drupal_initialize_schema($module, &$schema) {
} }
} }
/**
* Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query.
*
* @param $table
* The name of the table from which to retrieve fields.
* @param
* An optional prefix to to all fields.
*
* @return An array of fields.
**/
function drupal_schema_fields_sql($table, $prefix = NULL) {
$schema = drupal_get_schema($table);
$fields = array_keys($schema['fields']);
if ($prefix) {
$columns = array();
foreach ($fields as $field) {
$columns[] = "$prefix.$field";
}
return $columns;
}
else {
return $fields;
}
}
/**
* Save a record to the database based upon the schema. Default values are
* filled in for missing items, and 'serial' (auto increment) types are
* filled in with IDs.
*
* @param $table
* The name of the table; this must exist in schema API.
* @param $object
* The object to write. This is a reference, as defaults according to
* the schema may be filled in on the object, as well as ID on the serial
* type(s). Both array an object types may be passed.
* @param update
* If this is an update, specify the primary keys' field names. It is the
* caller's responsibility to know if a record for this object already
* exists in the database. If there is only 1 key, you may pass a simple string.
* @return (boolean) Failure to write a record will return FALSE. Otherwise,
* TRUE is returned. The $object parameter contains values for any serial
* fields defined by the $table. For example, $object->nid will be populated
* after inserting a new node.
*/
function drupal_write_record($table, &$object, $update = array()) {
// Standardize $update to an array.
if (is_string($update)) {
$update = array($update);
}
// Convert to an object if needed.
if (is_array($object)) {
$object = (object) $object;
$array = TRUE;
}
else {
$array = FALSE;
}
$schema = drupal_get_schema($table);
if (empty($schema)) {
return FALSE;
}
$fields = $defs = $values = $serials = array();
// Go through our schema, build SQL, and when inserting, fill in defaults for
// fields that are not set.
foreach ($schema['fields'] as $field => $info) {
// Special case -- skip serial types if we are updating.
if ($info['type'] == 'serial' && count($update)) {
continue;
}
// For inserts, populate defaults from Schema if not already provided
if (!isset($object->$field) && !count($update) && isset($info['default'])) {
$object->$field = $info['default'];
}
// Track serial fields so we can helpfully populate them after the query.
if ($info['type'] == 'serial') {
$serials[] = $field;
// Ignore values for serials when inserting data. Unsupported.
$object->$field = 'NULL';
}
// Build arrays for the fields, placeholders, and values in our query.
if (isset($object->$field)) {
$fields[] = $field;
$placeholders[] = db_type_placeholder($info['type']);
if (empty($info['serialize'])) {
$values[] = $object->$field;
}
else {
$values[] = serialize($object->$field);
}
}
}
// Build the SQL.
$query = '';
if (!count($update)) {
$query = "INSERT INTO {$table} (" . implode(', ', $fields) . ') VALUES (' . implode(', ', $placeholders) . ')';
$return = SAVED_NEW;
}
else {
$query = '';
foreach ($fields as $id => $field) {
if ($query) {
$query .= ', ';
}
$query .= $field . ' = ' . $placeholders[$id];
}
foreach ($update as $key){
$conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']);
$values[] = $object->$key;
}
$query = "UPDATE {$table} SET $query WHERE ". implode(' AND ', $conditions);
$return = SAVED_UPDATED;
}
db_query($query, $values);
if ($serials) {
// Get last insert ids and fill them in.
foreach ($serials as $field) {
$object->$field = db_last_insert_id($table, $field);
}
}
// If we began with an array, convert back so we don't surprise the caller.
if ($array) {
$object = (array)$object;
}
return $return;
}
/** /**
* @} End of "ingroup schemaapi". * @} End of "ingroup schemaapi".
*/ */

View File

@ -199,7 +199,7 @@ function _db_query_callback($match, $init = FALSE) {
* The Schema API type of a field (e.g. 'int', 'text', or 'varchar'). * The Schema API type of a field (e.g. 'int', 'text', or 'varchar').
*/ */
function db_placeholders($arguments, $type = 'int') { function db_placeholders($arguments, $type = 'int') {
$placeholder = _db_type_placeholder($type); $placeholder = db_type_placeholder($type);
return implode(',', array_fill(0, count($arguments), $placeholder)); return implode(',', array_fill(0, count($arguments), $placeholder));
} }
@ -487,7 +487,7 @@ function db_field_names($fields) {
* @return * @return
* The placeholder string to embed in a query for that type. * The placeholder string to embed in a query for that type.
*/ */
function _db_type_placeholder($type) { function db_type_placeholder($type) {
switch ($type) { switch ($type) {
case 'varchar': case 'varchar':
case 'text': case 'text':
@ -516,7 +516,7 @@ function _db_type_placeholder($type) {
// There is no safe value to return here, so return something that // There is no safe value to return here, so return something that
// will cause the query to fail. // will cause the query to fail.
return 'unsupported type '. $type .'for _db_type_placeholder'; return 'unsupported type '. $type .'for db_type_placeholder';
} }
/** /**

View File

@ -297,7 +297,7 @@ function db_add_field(&$ret, $table, $field, $spec, $keys_new = array()) {
$ret[] = update_sql($query); $ret[] = update_sql($query);
if (isset($spec['initial'])) { if (isset($spec['initial'])) {
// All this because update_sql does not support %-placeholders. // All this because update_sql does not support %-placeholders.
$sql = 'UPDATE {'. $table .'} SET '. $field .' = '. _db_type_placeholder($spec['type']); $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']);
$result = db_query($sql, $spec['initial']); $result = db_query($sql, $spec['initial']);
$ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')')); $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
} }

View File

@ -700,7 +700,7 @@ function db_add_field(&$ret, $table, $field, $spec, $new_keys = array()) {
$ret[] = update_sql($query); $ret[] = update_sql($query);
if (isset($spec['initial'])) { if (isset($spec['initial'])) {
// All this because update_sql does not support %-placeholders. // All this because update_sql does not support %-placeholders.
$sql = 'UPDATE {'. $table .'} SET '. $field .' = '. _db_type_placeholder($spec['type']); $sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']);
$result = db_query($sql, $spec['initial']); $result = db_query($sql, $spec['initial']);
$ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')')); $ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
} }

View File

@ -574,8 +574,10 @@ function file_save_upload($source, $validators = array(), $dest = FALSE, $replac
} }
// If we made it this far it's safe to record this file in the database. // If we made it this far it's safe to record this file in the database.
db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, '%s', '%s', '%s', %d, %d, %d)", $user->uid, $file->filename, $file->filepath, $file->filemime, $file->filesize, FILE_STATUS_TEMPORARY, time()); $file->uid = $user->uid;
$file->fid = db_last_insert_id('files', 'fid'); $file->status = FILE_STATUS_TEMPORARY;
$file->timestamp = time();
drupal_write_record('files', $file);
// Add file to the cache. // Add file to the cache.
$upload_cache[$source] = $file; $upload_cache[$source] = $file;

View File

@ -652,14 +652,22 @@ function node_load($param = array(), $revision = NULL, $reset = NULL) {
return FALSE; return FALSE;
} }
// Retrieve a field list based on the site's schema.
$fields = drupal_schema_fields_sql('node', 'n');
$fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r'));
$fields = array_merge($fields, array('u.name', 'u.data'));
$fields = implode(', ', $fields);
// rename timestamp field for clarity.
$fields = str_replace('r.timestamp', 'r.timestamp AS revision_timestamp', $fields);
// Retrieve the node. // Retrieve the node.
// No db_rewrite_sql is applied so as to get complete indexing for search. // No db_rewrite_sql is applied so as to get complete indexing for search.
if ($revision) { if ($revision) {
array_unshift($arguments, $revision); array_unshift($arguments, $revision);
$node = db_fetch_object(db_query('SELECT n.nid, r.vid, n.type, n.status, n.language, n.created, n.changed, n.comment, n.promote, n.sticky, n.tnid, n.translate, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. $cond, $arguments)); $node = db_fetch_object(db_query('SELECT '. $fields. ' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. $cond, $arguments));
} }
else { else {
$node = db_fetch_object(db_query('SELECT n.nid, n.vid, n.type, n.status, n.language, n.created, n.changed, n.comment, n.promote, n.sticky, n.tnid, n.translate, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. $cond, $arguments)); $node = db_fetch_object(db_query('SELECT '. $fields. ' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. $cond, $arguments));
} }
if ($node && $node->nid) { if ($node && $node->nid) {
@ -755,7 +763,6 @@ function node_submit($node) {
$node->uid = 0; $node->uid = 0;
} }
} }
$node->created = !empty($node->date) ? strtotime($node->date) : time(); $node->created = !empty($node->date) ? strtotime($node->date) : time();
$node->validated = TRUE; $node->validated = TRUE;
@ -777,16 +784,13 @@ function node_save(&$node) {
// Insert a new node. // Insert a new node.
$node->is_new = TRUE; $node->is_new = TRUE;
} }
elseif (!empty($node->revision)) {
$node->old_vid = $node->vid;
}
else { else {
// We need to ensure that all node fields are filled. // When updating a node, avoid clobberring an existing log entry with an empty one.
$node_current = node_load($node->nid); if (empty($node->log)) {
foreach ($node as $field => $data) { unset($node->log);
$node_current->$field = $data;
}
$node = $node_current;
if (!empty($node->revision)) {
$node->old_vid = $node->vid;
} }
} }
@ -797,65 +801,24 @@ function node_save(&$node) {
// The changed timestamp is always updated for bookkeeping purposes (revisions, searching, ...) // The changed timestamp is always updated for bookkeeping purposes (revisions, searching, ...)
$node->changed = time(); $node->changed = time();
// Split off revisions data to another structure $node->timestamp = time();
$revisions_table_values = array('nid' => &$node->nid, $node->format = isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT;
'title' => $node->title, 'body' => isset($node->body) ? $node->body : '',
'teaser' => $node->teaser, 'timestamp' => $node->changed,
'uid' => $user->uid, 'format' => isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT);
$revisions_table_types = array('nid' => '%d',
'title' => "'%s'", 'body' => "'%s'",
'teaser' => "'%s'", 'timestamp' => '%d',
'uid' => '%d', 'format' => '%d');
if (!empty($node->log) || $node->is_new || (isset($node->revision) && $node->revision)) {
// Only store the log message if there's something to store; this prevents
// existing log messages from being unintentionally overwritten by a blank
// message. A new revision will have an empty log message (or $node->log).
$revisions_table_values['log'] = !empty($node->log) ? $node->log : '';
$revisions_table_types['log'] = "'%s'";
}
$node_table_values = array(
'title' => $node->title, 'type' => $node->type, 'uid' => $node->uid,
'status' => $node->status, 'language' => $node->language, 'created' => $node->created,
'changed' => $node->changed, 'comment' => $node->comment,
'promote' => $node->promote, 'sticky' => $node->sticky);
$node_table_types = array(
'title' => "'%s'", 'type' => "'%s'", 'uid' => '%d',
'status' => '%d', 'language' => "'%s'", 'created' => '%d',
'changed' => '%d', 'comment' => '%d',
'promote' => '%d', 'sticky' => '%d');
$update_node = TRUE; $update_node = TRUE;
//Generate the node table query and the //Generate the node table query and the
//the node_revisions table query //the node_revisions table query
if ($node->is_new) { if ($node->is_new) {
$node_query = 'INSERT INTO {node} ('. implode(', ', array_keys($node_table_types)) .') VALUES ('. implode(', ', $node_table_types) .')'; drupal_write_record('node', $node);
db_query($node_query, $node_table_values); drupal_write_record('node_revisions', $node);
$node->nid = db_last_insert_id('node', 'nid');
$revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')';
db_query($revisions_query, $revisions_table_values);
$node->vid = db_last_insert_id('node_revisions', 'vid');
$op = 'insert'; $op = 'insert';
} }
else { else {
$arr = array(); drupal_write_record('node', $node, 'nid');
foreach ($node_table_types as $key => $value) {
$arr[] = $key .' = '. $value;
}
$node_table_values[] = $node->nid;
$node_query = 'UPDATE {node} SET '. implode(', ', $arr) .' WHERE nid = %d';
db_query($node_query, $node_table_values);
if (!empty($node->revision)) { if (!empty($node->revision)) {
$revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')'; drupal_write_record('node_revisions', $node);
db_query($revisions_query, $revisions_table_values);
$node->vid = db_last_insert_id('node_revisions', 'vid');
} }
else { else {
$arr = array(); drupal_write_record('node_revisions', $node, 'vid');
foreach ($revisions_table_types as $key => $value) {
$arr[] = $key .' = '. $value;
}
$revisions_table_values[] = $node->vid;
$revisions_query = 'UPDATE {node_revisions} SET '. implode(', ', $arr) .' WHERE vid = %d';
db_query($revisions_query, $revisions_table_values);
$update_node = FALSE; $update_node = FALSE;
} }
$op = 'update'; $op = 'update';

View File

@ -180,7 +180,7 @@ function taxonomy_overview_terms($vocabulary) {
if ($vocabulary->tags) { if ($vocabulary->tags) {
// We are not calling taxonomy_get_tree because that might fail with a big // We are not calling taxonomy_get_tree because that might fail with a big
// number of tags in the freetagging vocabulary. // number of tags in the freetagging vocabulary.
$results = pager_query(db_rewrite_sql('SELECT t.*, h.parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $page_increment, 0, NULL, $vid); $results = pager_query(db_rewrite_sql('SELECT t.*, h.parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $page_increment, 0, NULL, $vocabulary->vid);
while ($term = db_fetch_object($results)) { while ($term = db_fetch_object($results)) {
$rows[] = array( $rows[] = array(
l($term->name, "taxonomy/term/$term->tid"), l($term->name, "taxonomy/term/$term->tid"),
@ -207,7 +207,7 @@ function taxonomy_overview_terms($vocabulary) {
$GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME
} }
$output .= theme('table', $header, $rows, array('id' => 'taxonomy')); $output = theme('table', $header, $rows, array('id' => 'taxonomy'));
if ($vocabulary->tags || $total_entries >= $page_increment) { if ($vocabulary->tags || $total_entries >= $page_increment) {
$output .= theme('pager', NULL, $page_increment); $output .= theme('pager', NULL, $page_increment);
} }

View File

@ -165,8 +165,12 @@ function taxonomy_menu() {
function taxonomy_save_vocabulary(&$edit) { function taxonomy_save_vocabulary(&$edit) {
$edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes']; $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
if (!isset($edit['module'])) {
$edit['module'] = 'taxonomy';
}
if (!empty($edit['vid']) && !empty($edit['name'])) { if (!empty($edit['vid']) && !empty($edit['name'])) {
db_query("UPDATE {vocabulary} SET name = '%s', description = '%s', help = '%s', multiple = %d, required = %d, hierarchy = %d, relations = %d, tags = %d, weight = %d, module = '%s' WHERE vid = %d", $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy', $edit['vid']); drupal_write_record('vocabulary', $edit, 'vid');
db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']); db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
foreach ($edit['nodes'] as $type => $selected) { foreach ($edit['nodes'] as $type => $selected) {
db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type); db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
@ -178,8 +182,7 @@ function taxonomy_save_vocabulary(&$edit) {
$status = taxonomy_del_vocabulary($edit['vid']); $status = taxonomy_del_vocabulary($edit['vid']);
} }
else { else {
db_query("INSERT INTO {vocabulary} (name, description, help, multiple, required, hierarchy, relations, tags, weight, module) VALUES ('%s', '%s', '%s', %d, %d, %d, %d, %d, %d, '%s')", $edit['name'], isset($edit['description']) ? $edit['description'] : NULL, isset($edit['help']) ? $edit['help'] : NULL, $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], isset($edit['tags']) ? $edit['tags'] : NULL, $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy'); drupal_write_record('vocabulary', $edit);
$edit['vid'] = db_last_insert_id('vocabulary', 'vid');
foreach ($edit['nodes'] as $type => $selected) { foreach ($edit['nodes'] as $type => $selected) {
db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type); db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
} }
@ -231,7 +234,7 @@ function taxonomy_save_term(&$form_values) {
); );
if (!empty($form_values['tid']) && $form_values['name']) { if (!empty($form_values['tid']) && $form_values['name']) {
db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $form_values['name'], $form_values['description'], $form_values['weight'], $form_values['tid']); drupal_write_record('term_data', $form_values, 'tid');
$hook = 'update'; $hook = 'update';
$status = SAVED_UPDATED; $status = SAVED_UPDATED;
} }
@ -239,8 +242,7 @@ function taxonomy_save_term(&$form_values) {
return taxonomy_del_term($form_values['tid']); return taxonomy_del_term($form_values['tid']);
} }
else { else {
db_query("INSERT INTO {term_data} (name, description, vid, weight) VALUES ('%s', '%s', %d, %d)", $form_values['name'], $form_values['description'], $form_values['vid'], $form_values['weight']); drupal_write_record('term_data', $form_values);
$form_values['tid'] = db_last_insert_id('term_data', 'tid');
$hook = 'insert'; $hook = 'insert';
$status = SAVED_NEW; $status = SAVED_NEW;
} }