summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-05-01 15:28:13 +0000
committerDries Buytaert <dries@buytaert.net>2009-05-01 15:28:13 +0000
commit9fd65b6617f89e918392c9ea8c4ebc93fb4687e6 (patch)
tree7fb381513cb7f5b070e58938a11a3b69a3ef5a05
parent81c436fb8ede77a655a79bca6d613e205b913e86 (diff)
downloadbrdo-9fd65b6617f89e918392c9ea8c4ebc93fb4687e6.tar.gz
brdo-9fd65b6617f89e918392c9ea8c4ebc93fb4687e6.tar.bz2
- Patch #392696 by yched et al: save default values on insert.
-rw-r--r--modules/field/field.attach.inc32
-rw-r--r--modules/field/field.autoload.inc11
-rw-r--r--modules/field/field.default.inc33
-rw-r--r--modules/field/field.form.inc5
-rw-r--r--modules/field/field.test81
5 files changed, 115 insertions, 47 deletions
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index 72f1d4f1b..b95d083d0 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -183,10 +183,9 @@ function _field_invoke($op, $obj_type, &$object, &$a = NULL, &$b = NULL, $defaul
$return[] = $result;
}
}
- // Put back the altered items in the object, if the field was present to
- // begin with (avoid replacing missing field with empty array(), those are
- // not semantically equivalent on update).
- if (isset($object->$field_name)) {
+ // Populate $items back in the field values, but avoid replacing missing
+ // fields with an empty array (those are not equivalent on update).
+ if ($items !== array() || property_exists($object, $field_name)) {
$object->$field_name = $items;
}
}
@@ -500,28 +499,35 @@ function _field_attach_presave($obj_type, &$object) {
}
/**
- * Save field data for a new object. The passed in object must
- * already contain its id and (if applicable) revision id attributes.
+ * Save field data for a new object.
+ *
+ * The passed in object must already contain its id and (if applicable)
+ * revision id attributes.
+ * Default values (if any) will be saved for fields not present in the
+ * $object.
*
* @param $obj_type
* The type of $object; e.g. 'node' or 'user'.
* @param $object
* The object with fields to save.
+ * @return
+ * Default values (if any) will be added to the $object parameter for fields
+ * it leaves unspecified.
*/
function _field_attach_insert($obj_type, &$object) {
-
+ _field_invoke_default('insert', $obj_type, $object);
_field_invoke('insert', $obj_type, $object);
// Let other modules act on inserting the object, accumulating saved
// fields along the way.
- $saved = array();
+ $skip_fields = array();
foreach (module_implements('field_attach_pre_insert') as $module) {
$function = $module . '_field_attach_pre_insert';
- $function($obj_type, $object, $saved);
+ $function($obj_type, $object, $skip_fields);
}
// Field storage module saves any remaining unsaved fields.
- module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_INSERT, $saved);
+ module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_INSERT, $skip_fields);
list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object);
if ($cacheable) {
@@ -543,14 +549,14 @@ function _field_attach_update($obj_type, &$object) {
// Let other modules act on updating the object, accumulating saved
// fields along the way.
- $saved = array();
+ $skip_fields = array();
foreach (module_implements('field_attach_pre_update') as $module) {
$function = $module . '_field_attach_pre_update';
- $function($obj_type, $object, $saved);
+ $function($obj_type, $object, $skip_fields);
}
// Field storage module saves any remaining unsaved fields.
- module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_UPDATE, $saved);
+ module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_write', $obj_type, $object, FIELD_STORAGE_UPDATE, $skip_fields);
list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object);
if ($cacheable) {
diff --git a/modules/field/field.autoload.inc b/modules/field/field.autoload.inc
index 9d848f65e..9ebe204f2 100644
--- a/modules/field/field.autoload.inc
+++ b/modules/field/field.autoload.inc
@@ -185,13 +185,20 @@ function field_attach_presave($obj_type, &$object) {
}
/**
- * Save field data for a new object. The passed in object must
- * already contain its id and (if applicable) revision id attributes.
+ * Save field data for a new object.
+ *
+ * The passed in object must already contain its id and (if applicable)
+ * revision id attributes.
+ * Default values (if any) will be inserted for fields not present in the
+ * $object.
*
* @param $obj_type
* The type of $object; e.g. 'node' or 'user'.
* @param $object
* The object with fields to save.
+ * @return
+ * Default values (if any) will be added to the $object parameter for fields
+ * it leaves unspecified.
*
* This function is an autoloader for _field_attach_insert() in modules/field/field.attach.inc.
*/
diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc
index 98d7cc371..74d853ade 100644
--- a/modules/field/field.default.inc
+++ b/modules/field/field.default.inc
@@ -3,11 +3,12 @@
/**
* @file
- * Default 'implementations' of hook_field_*().
+ * Default 'implementations' of hook_field_*(): common field housekeeping.
*
- * Handles common field housekeeping.
* Those implementations are special, as field.module does not define any field
* types. Those functions take care of default stuff common to all field types.
+ * They are called through the _field_invoke_default() iterator, generally in
+ * the corresponding field_attach_[operation]() function.
*/
function field_default_extract_form_values($obj_type, $object, $field, $instance, &$items, $form, &$form_state) {
@@ -15,18 +16,8 @@ function field_default_extract_form_values($obj_type, $object, $field, $instance
if (isset($form_state['values'][$field_name])) {
$items = $form_state['values'][$field_name];
-
// Remove the 'value' of the 'add more' button.
unset($items[$field_name . '_add_more']);
-
- // _field_invoke() does not add back items for fields not present in the
- // original $object, so we add them manually.
- $object->{$field_name} = $items;
- }
- else {
- // The form did not include this field, for instance because of access
- // rules: make sure any existing value for the field stays unchanged.
- unset($object->{$field_name});
}
}
@@ -49,6 +40,24 @@ function field_default_submit($obj_type, &$object, $field, $instance, &$items, $
}
/**
+ * Default field 'insert' operation.
+ *
+ * Insert default value if no $object->$field_name entry was provided.
+ * This can happen with programmatic saves, or on form-based creation where
+ * the current user doesn't have 'edit' permission for the field.
+ */
+function field_default_insert($obj_type, &$object, $field, $instance, &$items) {
+ // _field_invoke() populates $items with an empty array if the $object has no
+ // entry for the field, so we check on the $object itself.
+ if (!property_exists($object, $field['field_name']) && !empty($instance['default_value_function'])) {
+ $function = $instance['default_value_function'];
+ if (drupal_function_exists($function)) {
+ $items = $function($obj_type, $object, $field, $instance);
+ }
+ }
+}
+
+/**
* The 'view' operation constructs the $object in a way that you can use
* drupal_render() to display the formatted output for an individual field.
* i.e. print drupal_render($object->content['field_foo']);
diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc
index b331e1fba..234eb69c2 100644
--- a/modules/field/field.form.inc
+++ b/modules/field/field.form.inc
@@ -22,9 +22,8 @@ function field_default_form($obj_type, $object, $field, $instance, $items, &$for
$field_name = $field['field_name'];
// If the field is not accessible, don't add anything. The field value will
- // be left unchanged on update, or considered empty on insert.
- // TODO : if/when field_attach_insert() takes care of default values,
- // unaccessible fields will automatically get the default value on insert.
+ // be left unchanged on update, or considered empty on insert (default value
+ // will be inserted if applicable).
if (!field_access('edit', $field)) {
return $addition;
}
diff --git a/modules/field/field.test b/modules/field/field.test
index 917654721..64a13f323 100644
--- a/modules/field/field.test
+++ b/modules/field/field.test
@@ -116,39 +116,86 @@ class FieldAttachTestCase extends DrupalWebTestCase {
*/
function testFieldAttachSaveMissingData() {
$entity_type = 'test_entity';
- $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $entity_init = field_test_create_stub_entity();
// Insert: Field is missing.
+ $entity = clone($entity_init);
field_attach_insert($entity_type, $entity);
- field_attach_load($entity_type, array(0 => $entity));
- $this->assertEqual(count($entity->{$this->field_name}), 0, 'Missing field results in no inserts');
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: missing field results in no value saved'));
// Insert: Field is NULL.
+ field_cache_clear();
+ $entity = clone($entity_init);
$entity->{$this->field_name} = NULL;
field_attach_insert($entity_type, $entity);
- field_attach_load($entity_type, array(0 => $entity));
- $this->assertTrue($entity->{$this->field_name} == NULL, 'NULL field results in no inserts');
- // Add some real data
- $value = 5;
- $entity->{$this->field_name} = array(0 => array('value' => $value));
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
+
+ // Add some real data.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ $values = array(0 => array('value' => mt_rand(1, 127)));
+ $entity->{$this->field_name} = $values;
field_attach_insert($entity_type, $entity);
- $entity->{$this->field_name} = NULL;
- field_attach_load($entity_type, array(0 => $entity));
- $this->assertTrue($entity->{$this->field_name}[0]['value'] == $value, 'Field data saved');
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertEqual($entity->{$this->field_name}, $values, t('Field data saved'));
// Update: Field is missing. Data should survive.
- unset($entity->{$this->field_name});
+ field_cache_clear();
+ $entity = clone($entity_init);
field_attach_update($entity_type, $entity);
- $entity->{$this->field_name} = NULL;
- field_attach_load($entity_type, array(0 => $entity));
- $this->assertEqual($entity->{$this->field_name}[0]['value'], $value, 'Field missing data survives');
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertEqual($entity->{$this->field_name}, $values, t('Update: missing field leaves existing values in place'));
// Update: Field is NULL. Data should be wiped.
+ field_cache_clear();
+ $entity = clone($entity_init);
$entity->{$this->field_name} = NULL;
field_attach_update($entity_type, $entity);
- field_attach_load($entity_type, array(0 => $entity));
- $this->assertTrue($entity->{$this->field_name} == NULL, 'NULL field update');
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), t('Update: NULL field removes existing values'));
+ }
+
+ /**
+ * Test insert with missing or NULL fields, with default value.
+ */
+ function testFieldAttachSaveMissingDataDefaultValue() {
+ // Add a default value.
+ $this->instance['default_value_function'] = 'field_test_default_value';
+ field_update_instance($this->instance);
+
+ $entity_type = 'test_entity';
+ $entity_init = field_test_create_stub_entity();
+
+ // Insert: Field is NULL.
+ $entity = clone($entity_init);
+ $entity->{$this->field_name} = NULL;
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $this->assertTrue(empty($entity->{$this->field_name}), t('Insert: NULL field results in no value saved'));
+
+ // Insert: Field is missing.
+ field_cache_clear();
+ $entity = clone($entity_init);
+ field_attach_insert($entity_type, $entity);
+
+ $entity = clone($entity_init);
+ field_attach_load($entity_type, array($entity->ftid => $entity));
+ $values = field_test_default_value($entity_type, $entity, $this->field, $this->instance);
+ $this->assertEqual($entity->{$this->field_name}, $values, t('Insert: missing field results in default value saved'));
}
function testFieldAttachViewAndPreprocess() {