#362024 by neclimdul, yched, and bjaspan: Make hook_field_load() multiple like field_attach_load().

merge-requests/26/head
Angie Byron 2009-05-17 00:32:29 +00:00
parent ba61b42bd9
commit f180449767
6 changed files with 354 additions and 182 deletions

View File

@ -188,18 +188,32 @@ function hook_field_formatter_info() {
/**
* Define custom load behavior for this module's field types.
*
* Unlike other field hooks, this hook operates on multiple objects. The
* $objects, $instances and $items parameters are arrays keyed by object id.
* For performance reasons, information for all available objects should be
* loaded in a single query where possible.
*
* Note that the changes made to the field values get cached by the
* field cache for subsequent loads.
*
* @param $obj_type
* The type of $object.
* @param $object
* The object for the operation.
* @param $objects
* Array of objects being loaded, keyed by object id.
* @param $field
* The field structure for the operation.
* @param $instance
* The instance structure for $field on $object's bundle.
* @param $instances
* Array of instance structures for $field for each object, keyed by object id.
* @param $items
* $object->{$field['field_name']}, or an empty array if unset.
* Array of field values already loaded for the objects, keyed by object id.
* @param $age
* FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
* FIELD_LOAD_REVISION to load the version indicated by each object.
* @return
* Changes or additions to field values are done by altering the $items
* parameter by reference.
*/
function hook_field_load($obj_type, $object, $field, $instance, $items) {
function hook_field_load($obj_type, $objects, $field, $instances, &$items, $age) {
}
/**
@ -452,23 +466,20 @@ function hook_field_attach_form($obj_type, $object, &$form, &$form_state) {
* @param $obj_type
* The type of objects for which to load fields; e.g. 'node' or 'user'.
* @param $objects
* An array of objects for which to load fields. The keys for primary id and
* bundle name to load are identified by hook_fieldable_info for $obj_type.
* An array of objects for which to load fields, keyed by object id.
* @param $age
* FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
* FIELD_LOAD_REVISION to load the version indicated by each object.
* @param $additions
* An array of field data for the objects being loaded, keyed by entity id,
* field name, and item delta number.
* @param $skip_fields
* An array keyed by names of fields whose data has already been loaded and
* therefore should not be loaded again. The values associated to these keys
* are not specified.
* @return
* Loaded field values are added to $additions and loaded field names are set
* as keys in $skip_fields.
* - Loaded field values are added to $objects. Fields with no values should be
* set as an empty array.
* - Loaded field names are set as keys in $skip_fields.
*/
function hook_field_attach_pre_load($obj_type, $objects, $age, &$additions, &$skip_fields) {
function hook_field_attach_pre_load($obj_type, &$objects, $age, &$skip_fields) {
}
/**
@ -476,10 +487,17 @@ function hook_field_attach_pre_load($obj_type, $objects, $age, &$additions, &$sk
*
* This hook is invoked after the field module has performed the operation.
*
* Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
* It takes an array of objects indexed by object id as its first parameter.
* For performance reasons, information for all available objects should be
* loaded in a single query where possible.
*
* Note that the changes made to the objects' field values get cached by the
* field cache for subsequent loads.
*
* See field_attach_load() for details and arguments.
* TODO: Currently, this hook only accepts a single object a time.
*/
function hook_field_attach_load($obj_type, $object) {
function hook_field_attach_load($obj_type, &$objects, $age) {
}
/**
@ -640,10 +658,9 @@ function hook_field_attach_delete_bundle($bundle, $instances) {
* Load field data for a set of objects.
*
* @param $obj_type
* The entity type of objects being loaded, such as 'node' or
* 'user'.
* The entity type of object, such as 'node' or 'user'.
* @param $objects
* The array of objects for which to load data.
* The array of objects for which to load data, keyed by object id.
* @param $age
* FIELD_LOAD_CURRENT to load the most recent revision for all
* fields, or FIELD_LOAD_REVISION to load the version indicated by
@ -653,10 +670,10 @@ function hook_field_attach_delete_bundle($bundle, $instances) {
* therefore should not be loaded again. The values associated to these keys
* are not specified.
* @return
* An array of field data for the objects, keyed by entity id, field
* name, and item delta number.
* Loaded field values are added to $objects. Fields with no values should be
* set as an empty array.
*/
function hook_field_storage_load($obj_type, $queried_objs, $age, $skip_fields) {
function hook_field_storage_load($obj_type, &$objects, $age, $skip_fields) {
}
/**

View File

@ -102,7 +102,7 @@ define('FIELD_STORAGE_INSERT', 'insert');
* for any object after the operation is complete, and access or
* modify all the field, form, or display data for that object and
* operation. For example, field_attach_view() invokes
* hook_field_attach_view_alter(). These all-module hooks are distinct from
* hook_field_attach_view_alter(). These all-module hooks are distinct from
* those of the Field Types API, such as hook_field_load(), that are
* only invoked for the module that defines a specific field type.
*
@ -127,8 +127,7 @@ define('FIELD_STORAGE_INSERT', 'insert');
* Invoke a field hook.
*
* @param $op
* - Possible operations include:
* - load
* Possible operations include:
* - form
* - validate
* - presave
@ -140,28 +139,21 @@ define('FIELD_STORAGE_INSERT', 'insert');
* - view
* - preprocess
* - prepare translation
*
* @param $obj_type
* - Can be:
* - node
* - user
* - Others not yet implemented.
*
* The type of $object; e.g. 'node' or 'user'.
* @param $object
* - The fully formed $obj_type object.
*
* The fully formed $obj_type object.
* @param $a
* - The $form in the 'form' operation.
* - The value of $teaser in the 'view' operation.
* - Otherwise NULL.
*
* - The $form in the 'form' operation.
* - The value of $teaser in the 'view' operation.
* - Otherwise NULL.
* @param $b
* - The $form_state in the 'submit' operation.
* - Otherwise NULL.
* - The $form_state in the 'submit' operation.
* - Otherwise NULL.
*
* @param $default
* - TRUE: render the default field implementation of the field hook.
* - FALSE: render the field module's implementation of the field hook.
* - TRUE: use the default field implementation of the field hook.
* - FALSE: use the field module's implementation of the field hook.
*/
function _field_invoke($op, $obj_type, &$object, &$a = NULL, &$b = NULL, $default = FALSE) {
list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
@ -179,11 +171,11 @@ function _field_invoke($op, $obj_type, &$object, &$a = NULL, &$b = NULL, $defaul
if (is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
elseif (isset($result)) {
$return[] = $result;
}
}
// Populate $items back in the field values, but avoid replacing missing
// Populate field values back in the object, 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;
@ -193,6 +185,85 @@ function _field_invoke($op, $obj_type, &$object, &$a = NULL, &$b = NULL, $defaul
return $return;
}
/**
* Invoke a field operation across fields on multiple objects.
*
* @param $op
* Possible operations include:
* - load
* @param $obj_type
* The type of $object; e.g. 'node' or 'user'.
* @param $objects
* An array of objects, keyed by object id.
* @param $a
* - The $age parameter in the 'load' operation.
* - Otherwise NULL.
* @param $b
* Currently always NULL.
* @param $default
* - TRUE: use the default field implementation of the field hook.
* - FALSE: use the field module's implementation of the field hook.
* @return
* An array of returned values keyed by object id.
*/
function _field_invoke_multiple($op, $obj_type, &$objects, &$a = NULL, &$b = NULL, $default = FALSE) {
$fields = array();
$grouped_instances = array();
$grouped_objects = array();
$grouped_items = array();
$return = array();
// Preparation:
// - Get the list of fields contained in the various bundles.
// - For each field, group the corresponding instances, objects and field
// values.
// - Initialize the return value for each object.
foreach ($objects as $object) {
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
foreach (field_info_instances($bundle) as $instance) {
$field_name = $instance['field_name'];
if (!isset($grouped_fields[$field_name])) {
$fields[$field_name] = field_info_field($field_name);
}
$grouped_instances[$field_name][$id] = $instance;
$grouped_objects[$field_name][$id] = &$objects[$id];
$grouped_items[$field_name][$id] = isset($object->$field_name) ? $object->$field_name : array();
}
$return[$id] = array();
}
// Call each field's operation.
foreach ($fields as $field_name => $field) {
if (!empty($field)) {
$function = $default ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
if (drupal_function_exists($function)) {
$results = $function($obj_type, $grouped_objects[$field_name], $field, $grouped_instances[$field_name], $grouped_items[$field_name], $a, $b);
// Merge results by object.
if (isset($results)) {
foreach ($results as $id => $result) {
if (is_array($result)) {
$return[$id] = array_merge($return[$id], $result);
}
else {
$return[$id][] = $result;
}
}
}
}
}
// Populate field values back in the objects, but avoid replacing missing
// fields with an empty array (those are not equivalent on update).
foreach ($grouped_objects[$field_name] as $id => $object) {
if ($grouped_items[$field_name][$id] !== array() || property_exists($object, $field_name)) {
$object->$field_name = $grouped_items[$field_name][$id];
}
}
}
return $return;
}
/**
* Invoke field.module's version of a field hook.
*/
@ -200,6 +271,13 @@ function _field_invoke_default($op, $obj_type, &$object, &$a = NULL, &$b = NULL)
return _field_invoke($op, $obj_type, $object, $a, $b, TRUE);
}
/**
* Invoke field.module's version of a field hook on multiple objects.
*/
function _field_invoke_multiple_default($op, $obj_type, &$objects, &$a = NULL, &$b = NULL) {
return _field_invoke_multiple($op, $obj_type, $objects, $a, $b, TRUE);
}
/**
* @} End of "defgroup field_attach"
*
@ -239,113 +317,79 @@ function _field_attach_form($obj_type, $object, &$form, $form_state) {
* objects of a single object type.
*
* @param $obj_type
* The type of objects for which to load fields; e.g. 'node' or
* 'user'.
* The type of $object; e.g. 'node' or 'user'.
* @param $objects
* An array of objects for which to load fields. The keys for
* primary id and bundle name to load are identified by
* hook_fieldable_info for $obj_type.
* An array of objects for which to load fields, keyed by object id.
* Each object needs to have its 'bundle key', 'id key' and (if applicable)
* 'revision key' filled.
* @param $age
* FIELD_LOAD_CURRENT to load the most recent revision for all
* fields, or FIELD_LOAD_REVISION to load the version indicated by
* each object. Defaults to FIELD_LOAD_CURRENT; use
* field_attach_load_revision() instead of passing FIELD_LOAD_REVISION.
* @returns
* On return, the objects in $objects are modified by having the
* appropriate set of fields added.
* Loaded field values are added to $objects. Fields with no values should be
* set as an empty array.
*/
function _field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) {
$queried_objects = array();
$info = field_info_fieldable_types($obj_type);
$cacheable = isset($info['cacheable']) ? $info['cacheable'] : FALSE;
// Fetch avaliable objects from cache.
foreach ($objects as $object) {
list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object);
$cid = "field:$obj_type:$id:$vid";
if ($cacheable && $cached = cache_get($cid, 'cache_field')) {
foreach ($cached->data as $key => $value) {
$object->$key = $value;
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
if ($cacheable && $cached = cache_get("field:$obj_type:$id:$vid", 'cache_field')) {
foreach ($cached->data as $field_name => $items) {
$object->$field_name = $items;
}
}
else {
$queried_objects[$id] = $objects[$id];
$queried_objects[$id] = $object;
}
}
// Fetch other objects from the database.
if ($queried_objects) {
// The loading order is:
// The invoke order is:
// - hook_field_attach_pre_load()
// - storage engine's hook_field_storage_load()
// - field-type modules hook_field_load()
// - field-type module's hook_field_load()
// - hook_field_attach_load()
// We need the raw additions to be able to cache them, so the hooks must
// not alter objects directly but return their additions. At each step,
// results are merged into the $queried_objects, and into the $additions
// array, that will eventually get cached.
// Invoke hook_field_attach_pre_load(): let any module load field
// data before the storage engine, accumulating along the way.
$additions_pre_load = array();
$skip_fields = array();
foreach (module_implements('field_attach_pre_load') as $module) {
$function = $module . '_field_attach_pre_load';
$function($obj_type, $queried_objects, $age, $additions_pre_load, $skip_fields);
$function($obj_type, $queried_objects, $age, $skip_fields);
}
// Invoke the storage engine's hook_field_storage_load(): the field storage
// engine loads the rest.
$additions = module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields);
module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_load', $obj_type, $queried_objects, $age, $skip_fields);
// First, merge the additions from the storage engine.
foreach ($additions as $id => $obj_additions) {
foreach ($obj_additions as $key => $value) {
$queried_objects[$id]->$key = $value;
}
}
// Then, merge the pre_load additions, so that they take precedence.
foreach ($additions_pre_load as $id => $obj_additions) {
foreach ($obj_additions as $key => $value) {
$queried_objects[$id]->$key = $value;
$additions[$id][$key] = $value;
}
// Invoke field-type module's hook_field_load().
_field_invoke_multiple('load', $obj_type, $queried_objects, $age);
// Invoke hook_field_attach_load(): let other modules act on loading the
// object.
foreach (module_implements('field_attach_load') as $module) {
$function = $module . '_field_attach_load';
$function($obj_type, $queried_objects, $age);
}
// TODO D7 : to be consistent we might want to make hook_field_load() accept
// multiple objects too. Which forbids going through _field_invoke(), but
// requires manually iterating the instances instead.
foreach ($queried_objects as $id => $object) {
list($id, $vid, $bundle, $cacheable) = field_attach_extract_ids($obj_type, $object);
// Make sure empty fields are present as empty arrays.
$instances = field_info_instances($bundle);
foreach ($instances as $instance) {
if (!isset($object->{$instance['field_name']})) {
$queried_objects[$id]->{$instance['field_name']} = array();
$additions[$id][$instance['field_name']] = array();
// Build cache data.
if ($cacheable) {
foreach ($queried_objects as $id => $object) {
$data = array();
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
$instances = field_info_instances($bundle);
foreach ($instances as $instance) {
$data[$instance['field_name']] = $queried_objects[$id]->{$instance['field_name']};
}
}
// Invoke field-type modules hook_field_load().
$custom_additions = _field_invoke('load', $obj_type, $object);
foreach ($custom_additions as $key => $value) {
$queried_objects[$id]->$key = $value;
$additions[$id][$key] = $value;
}
// Invoke hook_field_attach_load(): let other modules act on loading the
// object.
// TODO : this currently doesn't get cached (we cache $additions).
// This should either be called after we fetch from cache, or return an
// array of additions.
foreach (module_implements('field_attach_load') as $module) {
$function = $module . '_field_attach_load';
$function($obj_type, $queried_objects[$id]);
}
// Cache the data.
if ($cacheable) {
$cid = "field:$obj_type:$id:$vid";
$data = isset($additions[$id]) ? $additions[$id] : array();
cache_set($cid, $data, 'cache_field');
}
}
@ -356,13 +400,15 @@ function _field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) {
* Load all fields for a previous version of each of a set of
* objects of a single object type.
*
* Loading different versions of the same objects is not supported,
* and should be done by separate calls to the function.
*
* @param $obj_type
* The type of objects for which to load fields; e.g. 'node' or
* 'user'.
* The type of $object; e.g. 'node' or 'user'.
* @param $objects
* An array of objects for which to load fields. The keys for
* primary id, revision id, and bundle name to load are identified by
* hook_fieldable_info for $obj_type.
* An array of objects for which to load fields, keyed by object id.
* Each object needs to have its 'bundle key', 'id key' and 'revision key'
* filled.
* @returns
* On return, the objects in $objects are modified by having the
* appropriate set of fields added.

View File

@ -41,20 +41,19 @@ function field_attach_form($obj_type, $object, &$form, $form_state) {
* objects of a single object type.
*
* @param $obj_type
* The type of objects for which to load fields; e.g. 'node' or
* 'user'.
* The type of $object; e.g. 'node' or 'user'.
* @param $objects
* An array of objects for which to load fields. The keys for
* primary id and bundle name to load are identified by
* hook_fieldable_info for $obj_type.
* An array of objects for which to load fields, keyed by object id.
* Each object needs to have its 'bundle key', 'id key' and (if applicable)
* 'revision key' filled.
* @param $age
* FIELD_LOAD_CURRENT to load the most recent revision for all
* fields, or FIELD_LOAD_REVISION to load the version indicated by
* each object. Defaults to FIELD_LOAD_CURRENT; use
* field_attach_load_revision() instead of passing FIELD_LOAD_REVISION.
* @returns
* On return, the objects in $objects are modified by having the
* appropriate set of fields added.
* Loaded field values are added to $objects. Fields with no values should be
* set as an empty array.
*
* This function is an autoloader for _field_attach_load() in modules/field/field.attach.inc.
*/
@ -67,13 +66,15 @@ function field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) {
* Load all fields for a previous version of each of a set of
* objects of a single object type.
*
* Loading different versions of the same objects is not supported,
* and should be done by separate calls to the function.
*
* @param $obj_type
* The type of objects for which to load fields; e.g. 'node' or
* 'user'.
* The type of $object; e.g. 'node' or 'user'.
* @param $objects
* An array of objects for which to load fields. The keys for
* primary id, revision id, and bundle name to load are identified by
* hook_fieldable_info for $obj_type.
* An array of objects for which to load fields, keyed by object id.
* Each object needs to have its 'bundle key', 'id key' and 'revision key'
* filled.
* @returns
* On return, the objects in $objects are modified by having the
* appropriate set of fields added.

View File

@ -47,15 +47,6 @@ class FieldAttachTestCase extends DrupalWebTestCase {
field_create_instance($this->instance);
}
// function testFieldAttachLoadMultiple() {
// TODO : test the 'multiple' aspect of load:
// define 2 bundles, 3 fields
// bundle1 gets instances of field1, field2
// bundle2 gets instances of field1, field3
// load 2 entities (one for each bundle) in a single load
// check that everything gets loaded ok.
// }
/**
* Check field values insert, update and load.
*
@ -63,6 +54,11 @@ class FieldAttachTestCase extends DrupalWebTestCase {
* updates random field data and then loads and verifies the data.
*/
function testFieldAttachSaveLoad() {
// Configure the instance so that we test hook_field_load() (see
// field_test_field_load() in field_test.module).
$this->instance['settings']['test_hook_field_load'] = TRUE;
field_update_instance($this->instance);
$entity_type = 'test_entity';
$values = array();
@ -96,6 +92,8 @@ class FieldAttachTestCase extends DrupalWebTestCase {
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// The field value loaded matches the one inserted or updated.
$this->assertEqual($entity->{$this->field_name}[$delta]['value'] , $values[$current_revision][$delta]['value'], t('Currrent revision: expected value %delta was found.', array('%delta' => $delta)));
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Currrent revision: extra information for value %delta was found', array('%delta' => $delta)));
}
// Confirm each revision loads the correct data.
@ -107,6 +105,73 @@ class FieldAttachTestCase extends DrupalWebTestCase {
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
// The field value loaded matches the one inserted or updated.
$this->assertEqual($entity->{$this->field_name}[$delta]['value'], $values[$revision_id][$delta]['value'], t('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$this->field_name}[$delta]['additional_key'], 'additional_value', t('Revision %revision_id: extra information for value %delta was found', array('%revision_id' => $revision_id, '%delta' => $delta)));
}
}
}
/**
* Test the 'multiple' load feature.
*/
function testFieldAttachLoadMultiple() {
$entity_type = 'test_entity';
// Define 2 bundles.
$bundles = array(
1 => 'test_bundle_1',
2 => 'test_bundle_2',
);
field_test_create_bundle($bundles[1]);
field_test_create_bundle($bundles[2]);
// Define 3 fields:
// - field_1 is in bundle_1 and bundle_2,
// - field_2 is in bundle_1,
// - field_3 is in bundle_2.
$field_bundles_map = array(
1 => array(1, 2),
2 => array(1),
3 => array(2),
);
for ($i = 1; $i <= 3; $i++) {
$field_names[$i] = 'field_' . $i;
$field = array('field_name' => $field_names[$i], 'type' => 'test_field');
field_create_field($field);
foreach ($field_bundles_map[$i] as $bundle) {
$instance = array(
'field_name' => $field_names[$i],
'bundle' => $bundles[$bundle],
'settings' => array(
// Configure the instance so that we test hook_field_load()
// (see field_test_field_load() in field_test.module).
'test_hook_field_load' => TRUE,
),
);
field_create_instance($instance);
}
}
// Create one test entity per bundle, with random values.
foreach ($bundles as $index => $bundle) {
$entities[$index] = field_test_create_stub_entity($index, $index, $bundle);
$entity = clone($entities[$index]);
$instances = field_info_instances($bundle);
foreach ($instances as $field_name => $instance) {
$values[$index][$field_name] = mt_rand(1, 127);
$entity->$field_name = array(array('value' => $values[$index][$field_name]));
}
field_attach_insert($entity_type, $entity);
}
// Check that a single load correctly loads field values for both entities.
field_attach_load($entity_type, $entities);
foreach ($entities as $index => $entity) {
$instances = field_info_instances($bundles[$index]);
foreach ($instances as $field_name => $instance) {
// The field value loaded matches the one inserted.
$this->assertEqual($entity->{$field_name}[0]['value'], $values[$index][$field_name], t('Entity %index: expected value was found.', array('%index' => $index)));
// The value added in hook_field_load() is found.
$this->assertEqual($entity->{$field_name}[0]['additional_key'], 'additional_value', t('Entity %index: extra information was found', array('%index' => $index)));
}
}
}
@ -354,7 +419,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
// Create a new bundle. This has to be initiated by the module so that its
// hook_fieldable_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle, $this->randomName());
field_test_create_bundle($new_bundle);
// Add an instance to that bundle.
$this->instance['bundle'] = $new_bundle;
@ -394,7 +459,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
// Create a new bundle. This has to be initiated by the module so that its
// hook_fieldable_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle, $this->randomName());
field_test_create_bundle($new_bundle);
// Add an instance to that bundle.
$this->instance['bundle'] = $new_bundle;
@ -449,48 +514,58 @@ class FieldAttachTestCase extends DrupalWebTestCase {
$this->assertFalse(field_read_instance($field_name, $instance['bundle']), "Second field is deleted");
}
/**
* Test field cache.
*/
function testFieldAttachCache() {
// Create a revision
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
// Initialize random values and a test entity.
$entity_init = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$values = array();
for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
$values[$delta]['value'] = mt_rand(1, 127);
}
$entity->{$this->field_name} = $values;
$noncached_type = 'test_entity';
$cached_type = 'test_cacheable_entity';
// Non-cached type:
// Test non-cached object type.
$cid = "field:$noncached_type:0:0";
// Confirm no initial cache entry
// Confirm no initial cache entry.
$this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no initial cache entry');
// Save, and confirm no cache entry
// Save, and confirm no cache entry.
$entity = clone($entity_init);
$entity->{$this->field_name} = $values;
field_attach_insert($noncached_type, $entity);
$this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no cache entry on save');
// Load, and confirm no cache entry
// Load, and confirm no cache entry.
$entity = clone($entity_init);
field_attach_load($noncached_type, array(0 => $entity));
$this->assertFalse(cache_get($cid, 'cache_field'), 'Non-cached: no cache entry on load');
// Cached type:
// Test cached object type.
$cid = "field:$cached_type:0:0";
// Confirm no initial cache entry
// Confirm no initial cache entry.
$this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no initial cache entry');
// Save, and confirm no cache entry
// Save, and confirm no cache entry.
$entity = clone($entity_init);
$entity->{$this->field_name} = $values;
field_attach_insert($cached_type, $entity);
$this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on save');
// Load, and confirm cache entry
// Load, and confirm cache entry.
$entity = clone($entity_init);
field_attach_load($cached_type, array(0 => $entity));
$cache = cache_get($cid, 'cache_field');
$this->assertEqual($cache->data[$this->field_name], $values, 'Cached: correct cache entry on load');
// Delete, and confirm no cache entry
// Delete, and confirm no cache entry.
field_attach_delete($cached_type, $entity);
$this->assertFalse(cache_get($cid, 'cache_field'), 'Cached: no cache entry on save');
}
@ -1156,6 +1231,8 @@ class FieldInstanceTestCase extends DrupalWebTestCase {
field_create_instance($this->instance_definition);
$instance = field_read_instance($this->instance_definition['field_name'], $this->instance_definition['bundle']);
$field_type = field_info_field_types($this->field['type']);
$widget_type = field_info_widget_types($instance['widget']['type']);
$formatter_type = field_info_formatter_types($instance['display']['full']['type']);
// Check that default values are set.
$this->assertIdentical($instance['required'], FALSE, t('Required defaults to false.'));
@ -1163,21 +1240,17 @@ class FieldInstanceTestCase extends DrupalWebTestCase {
$this->assertIdentical($instance['description'], '', t('Description defaults to empty string.'));
// Check that default instance settings are set.
$settings = array('test_instance_setting' => 'dummy test string');
$this->assertIdentical($settings, $instance['settings'] , t('Default instance settings have been written.'));
$this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.'));
// Check that the widget is the default one.
$this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Default widget has been written.'));
// Check that default widget settings are set.
$settings = array('test_widget_setting' => 'dummy test string');
$this->assertIdentical($settings, $instance['widget']['settings'] , t('Default widget settings have been written.'));
$this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.'));
// Check that we have display info for 'full' build_mode.
$this->assertTrue(isset($instance['display']['full']), t('Display for "full" build_mode has been written.'));
// Check that the formatter is the default one.
$this->assertIdentical($instance['display']['full']['type'], $field_type['default_formatter'], t('Default formatter for "full" build_mode has been written.'));
// Check that the default formatter settings are set.
$info = field_info_formatter_types($instance['display']['full']['type']);
$settings = $info['settings'];
$this->assertIdentical($settings, $instance['display']['full']['settings'] , t('Default formatter settings for "full" build_mode have been written.'));
$this->assertIdentical($instance['display']['full']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" build_mode have been written.'));
// Guarantee that the field/bundle combination is unique.
try {

View File

@ -176,7 +176,7 @@ function field_sql_storage_field_storage_delete_field($field_name) {
/**
* Implementation of hook_field_storage_load().
*/
function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_fields = array()) {
function field_sql_storage_field_storage_load($obj_type, &$objects, $age, $skip_fields = array()) {
$etid = _field_sql_storage_etid($obj_type);
$load_current = $age == FIELD_LOAD_CURRENT;
@ -185,18 +185,16 @@ function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_f
$delta_count = array();
foreach ($objects as $obj) {
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $obj);
foreach (field_info_instances($bundle) as $instance) {
$field_ids[$instance['field_name']][] = $load_current ? $id : $vid;
$delta_count[$id][$instance['field_name']] = 0;
foreach (field_info_instances($bundle) as $field_name => $instance) {
if (!isset($skip_fields[$field_name])) {
$objects[$id]->{$field_name} = array();
$field_ids[$field_name][] = $load_current ? $id : $vid;
$delta_count[$id][$field_name] = 0;
}
}
}
$additions = array();
foreach ($field_ids as $field_name => $ids) {
if (isset($skip_fields[$field_name])) {
continue;
}
$field = field_info_field($field_name);
$table = $load_current ? _field_sql_storage_tablename($field_name) : _field_sql_storage_revision_tablename($field_name);
@ -218,12 +216,11 @@ function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_f
}
// Add the item to the field values for the entity.
$additions[$row->entity_id][$field_name][] = $item;
$objects[$row->entity_id]->{$field_name}[] = $item;
$delta_count[$row->entity_id][$field_name]++;
}
}
}
return $additions;
}
/**

View File

@ -83,14 +83,31 @@ function field_test_fieldable_info() {
);
}
function field_test_create_bundle($bundle, $text) {
/**
* Create a new bundle for test_entity objects.
*
* @param $bundle
* The machine-readable name of the bundle.
* @param $text
* The human-readable name of the bundle. If none is provided, the machine
* name will be used.
*/
function field_test_create_bundle($bundle, $text = NULL) {
$bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
$bundles += array($bundle => $text);
$bundles += array($bundle => $text ? $text : $bundle);
variable_set('field_test_bundles', $bundles);
field_attach_create_bundle($bundle);
}
/**
* Rename a bundle for test_entity objects.
*
* @param $bundle_old
* The machine-readable name of the bundle to rename.
* @param $bundle_new
* The new machine-readable name of the bundle.
*/
function field_test_rename_bundle($bundle_old, $bundle_new) {
$bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
$bundles[$bundle_new] = $bundles[$bundle_old];
@ -100,6 +117,12 @@ function field_test_rename_bundle($bundle_old, $bundle_new) {
field_attach_rename_bundle($bundle_old, $bundle_new);
}
/**
* Delete a bundle for test_entity objects.
*
* @param $bundle
* The machine-readable name of the bundle to delete.
*/
function field_test_delete_bundle($bundle) {
$bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
unset($bundles[$bundle]);
@ -311,9 +334,13 @@ function field_test_field_info() {
return array(
'test_field' => array(
'label' => t('Test Field'),
'description' => t('Stores the value 1.'),
'settings' => array('test_field_setting' => 'dummy test string'),
'instance_settings' => array('test_instance_setting' => 'dummy test string'),
'settings' => array(
'test_field_setting' => 'dummy test string',
),
'instance_settings' => array(
'test_instance_setting' => 'dummy test string',
'test_hook_field_load' => FALSE,
),
'default_widget' => 'test_field_widget',
'default_formatter' => 'field_test_default',
),
@ -332,13 +359,6 @@ function field_test_field_columns($field) {
return $columns;
}
/**
* Implementation of hook_instance_settings().
*/
function field_test_field_instance_settings($field_type) {
return array('test_instance_setting' => 'dummy test string');
}
/**
* Implementation of hook_field_validate().
*
@ -487,6 +507,24 @@ function field_test_field_formatter_info() {
);
}
/**
* Implementation of hook_field_load().
*/
function field_test_field_load($obj_type, $objects, $field, $instances, &$items, $age) {
foreach ($items as $id => $item) {
// To keep the test non-intrusive, only act for instances with the
// test_hook_field_load setting explicitly set to TRUE.
if ($instances[$id]['settings']['test_hook_field_load']) {
foreach ($item as $delta => $value) {
// Don't add anything on empty values.
if ($value) {
$items[$id][$delta]['additional_key'] = 'additional_value';
}
}
}
}
}
/**
* Implementation of hook_theme().
*/
@ -530,4 +568,4 @@ function theme_field_formatter_field_test_multiple($element) {
*/
function field_test_default_value($obj_type, $object, $field, $instance) {
return array(array('value' => 99));
}
}