#362024 by neclimdul, yched, and bjaspan: Make hook_field_load() multiple like field_attach_load().
parent
ba61b42bd9
commit
f180449767
|
|
@ -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) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue