Commit 3295e62a authored by alexpott's avatar alexpott

Issue #2194885 by Berdir | sun: Remove drupal_write_record().

parent 57926bf5
......@@ -306,198 +306,6 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
}
}
/**
* Retrieves a list of fields from a table schema.
*
* The returned list is suitable for use in an SQL query.
*
* @param string $table
* The name of the table from which to retrieve fields.
* @param string $prefix
* An optional prefix to to all fields.
*
* @return array
* An array of fields.
*/
function drupal_schema_fields_sql($table, $prefix = NULL) {
if (!$schema = drupal_get_schema($table)) {
return array();
}
$fields = array_keys($schema['fields']);
if ($prefix) {
$columns = array();
foreach ($fields as $field) {
$columns[] = "$prefix.$field";
}
return $columns;
}
else {
return $fields;
}
}
/**
* Saves (inserts or updates) a record to the database based upon the schema.
*
* Do not use drupal_write_record() within hook_update_N() functions, since the
* database schema cannot be relied upon when a user is running a series of
* updates. Instead, use db_insert() or db_update() to save the record.
*
* @param string $table
* The name of the table; this must be defined by a hook_schema()
* implementation.
* @param object|array $record
* An object or array representing the record to write, passed in by
* reference. If inserting a new record, values not provided in $record will
* be populated in $record and in the database with the default values from
* the schema, as well as a single serial (auto-increment) field
* (if present). If updating an existing record, only provided values are
* updated in the database, and $record is not modified.
* @param array $primary_keys
* To indicate that this is a new record to be inserted, omit this argument.
* If this is an update, this argument specifies the primary keys' field
* names. If there is only 1 field in the key, you may pass in a string; if
* there are multiple fields in the key, pass in an array.
*
* @return bool|int
* If the record insert or update failed, returns FALSE. If it succeeded,
* returns SAVED_NEW or SAVED_UPDATED, depending on the operation performed.
*/
function drupal_write_record($table, &$record, $primary_keys = array()) {
// Standardize $primary_keys to an array.
if (is_string($primary_keys)) {
$primary_keys = array($primary_keys);
}
$schema = drupal_get_schema($table);
if (empty($schema)) {
return FALSE;
}
$object = (object) $record;
$fields = array();
$default_fields = array();
// Go through the schema to determine fields to write.
foreach ($schema['fields'] as $field => $info) {
if ($info['type'] == 'serial') {
// Skip serial types if we are updating.
if (!empty($primary_keys)) {
continue;
}
// Track serial field so we can helpfully populate them after the query.
// NOTE: Each table should come with one serial field only.
$serial = $field;
}
// Skip field if it is in $primary_keys as it is unnecessary to update a
// field to the value it is already set to.
if (in_array($field, $primary_keys)) {
continue;
}
// Skip fields that are not provided, default values are already known
// by the database. property_exists() allows to explicitly set a value to
// NULL.
if (!property_exists($object, $field)) {
$default_fields[] = $field;
continue;
}
// However, if $object is an entity class instance, then class properties
// always exist, as they cannot be unset. Therefore, if $field is a serial
// type and the value is NULL, skip it.
// @see http://php.net/manual/en/function.property-exists.php
if ($info['type'] == 'serial' && !isset($object->$field)) {
$default_fields[] = $field;
continue;
}
// Build array of fields to update or insert.
if (empty($info['serialize'])) {
$fields[$field] = $object->$field;
}
else {
$fields[$field] = serialize($object->$field);
}
// Type cast to proper datatype, except when the value is NULL and the
// column allows this.
if (isset($object->$field) || !empty($info['not null'])) {
$fields[$field] = drupal_schema_get_field_value($info, $fields[$field]);
}
}
// Build the SQL.
if (empty($primary_keys)) {
// We are doing an insert.
$options = array('return' => Database::RETURN_INSERT_ID);
if (isset($serial) && isset($fields[$serial])) {
// If the serial column has been explicitly set with an ID, then we don't
// require the database to return the last insert id.
if ($fields[$serial]) {
$options['return'] = Database::RETURN_AFFECTED;
}
// If a serial column does exist with no value (i.e. 0) then remove it as
// the database will insert the correct value for us.
else {
unset($fields[$serial]);
}
}
// Create an INSERT query. useDefaults() is necessary for the SQL to be
// valid when $fields is empty.
$query = db_insert($table, $options)
->fields($fields)
->useDefaults($default_fields);
$return = SAVED_NEW;
}
else {
// Create an UPDATE query.
$query = db_update($table)->fields($fields);
foreach ($primary_keys as $key) {
$query->condition($key, $object->$key);
}
$return = SAVED_UPDATED;
}
// Execute the SQL.
if ($query_return = $query->execute()) {
if (isset($serial)) {
// If the database was not told to return the last insert id, it will be
// because we already know it.
if (isset($options) && $options['return'] != Database::RETURN_INSERT_ID) {
$object->$serial = $fields[$serial];
}
else {
$object->$serial = $query_return;
}
}
}
// If we have a single-field primary key but got no insert ID, the
// query failed. Note that we explicitly check for FALSE, because
// a valid update query which doesn't change any values will return
// zero (0) affected rows.
elseif ($query_return === FALSE && count($primary_keys) == 1) {
$return = FALSE;
}
// If we are inserting, populate empty fields with default values.
if (empty($primary_keys)) {
foreach ($schema['fields'] as $field => $info) {
if (isset($info['default']) && !property_exists($object, $field)) {
$object->$field = $info['default'];
}
}
}
// If we began with an array, convert back.
if (is_array($record)) {
$record = (array) $object;
}
return $return;
}
/**
* Typecasts values to proper datatypes.
*
......
<?php
/**
* @file
* Definition of Drupal\system\Tests\Common\WriteRecordTest.
*/
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests writing of data records with drupal_write_record().
*
* @group Common
*/
class WriteRecordTest extends DrupalUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('database_test');
/**
* Tests the drupal_write_record() API function.
*/
function testDrupalWriteRecord() {
$this->installSchema('database_test', array('test', 'test_null', 'test_serialized', 'test_composite_primary'));
// Insert a record with no columns populated.
$record = array();
$insert_result = drupal_write_record('test', $record);
$this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when an empty record is inserted with drupal_write_record().');
// Insert a record - no columns allow NULL values.
$person = new \stdClass();
$person->name = 'John';
$person->unknown_column = 123;
$insert_result = drupal_write_record('test', $person);
$this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.');
$this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
$this->assertIdentical($person->age, 0, 'Age field set to default value.');
$this->assertIdentical($person->job, 'Undefined', 'Job field set to default value.');
// Verify that the record was inserted.
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'John', 'Name field set.');
$this->assertIdentical($result->age, '0', 'Age field set to default value.');
$this->assertIdentical($result->job, 'Undefined', 'Job field set to default value.');
$this->assertFalse(isset($result->unknown_column), 'Unknown column was ignored.');
// Update the newly created record.
$person->name = 'Peter';
$person->age = 27;
$person->job = NULL;
$update_result = drupal_write_record('test', $person, array('id'));
$this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.');
// Verify that the record was updated.
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Peter', 'Name field set.');
$this->assertIdentical($result->age, '27', 'Age field set.');
$this->assertIdentical($result->job, '', 'Job field set and cast to string.');
// Try to insert NULL in columns that does not allow this.
$person = new \stdClass();
$person->name = 'Ringo';
$person->age = NULL;
$person->job = NULL;
drupal_write_record('test', $person);
$this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
$result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Ringo', 'Name field set.');
$this->assertIdentical($result->age, '0', 'Age field set.');
$this->assertIdentical($result->job, '', 'Job field set.');
// Insert a record - the "age" column allows NULL.
$person = new \stdClass();
$person->name = 'Paul';
$person->age = NULL;
drupal_write_record('test_null', $person);
$this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Paul', 'Name field set.');
$this->assertIdentical($result->age, NULL, 'Age field set.');
// Insert a record - do not specify the value of a column that allows NULL.
$person = new \stdClass();
$person->name = 'Meredith';
drupal_write_record('test_null', $person);
$this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
$this->assertIdentical($person->age, 0, 'Age field set to default value.');
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Meredith', 'Name field set.');
$this->assertIdentical($result->age, '0', 'Age field set to default value.');
// Update the newly created record.
$person->name = 'Mary';
$person->age = NULL;
drupal_write_record('test_null', $person, array('id'));
$result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Mary', 'Name field set.');
$this->assertIdentical($result->age, NULL, 'Age field set.');
// Insert a record - the "data" column should be serialized.
$person = new \stdClass();
$person->name = 'Dave';
drupal_write_record('test_serialized', $person);
$result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical($result->name, 'Dave', 'Name field set.');
$this->assertIdentical($result->info, NULL, 'Info field set.');
$person->info = array();
drupal_write_record('test_serialized', $person, array('id'));
$result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical(unserialize($result->info), array(), 'Info field updated.');
// Update the serialized record.
$data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL);
$person->info = $data;
drupal_write_record('test_serialized', $person, array('id'));
$result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
$this->assertIdentical(unserialize($result->info), $data, 'Info field updated.');
// Run an update query where no field values are changed. The database
// layer should return zero for number of affected rows, but
// db_write_record() should still return SAVED_UPDATED.
$update_result = drupal_write_record('test_null', $person, array('id'));
$this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a valid update is run without changing any values.');
// Insert an object record for a table with a multi-field primary key.
$composite_primary = new \stdClass();
$composite_primary->name = $this->randomMachineName();
$composite_primary->age = mt_rand();
$insert_result = drupal_write_record('test_composite_primary', $composite_primary);
$this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.');
// Update the record.
$update_result = drupal_write_record('test_composite_primary', $composite_primary, array('name', 'job'));
$this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment