summaryrefslogtreecommitdiff
path: root/modules/field/field.attach.inc
diff options
context:
space:
mode:
authorAngie Byron <webchick@24967.no-reply.drupal.org>2009-05-17 00:32:29 +0000
committerAngie Byron <webchick@24967.no-reply.drupal.org>2009-05-17 00:32:29 +0000
commitf180449767d54b14d024732f5ec1dd659b0b4a8d (patch)
treefe02b61b461c4e18edbb1125fd46fead487106b0 /modules/field/field.attach.inc
parentba61b42bd94d0c6af0de1fcba42d60bf3ceeb4c2 (diff)
downloadbrdo-f180449767d54b14d024732f5ec1dd659b0b4a8d.tar.gz
brdo-f180449767d54b14d024732f5ec1dd659b0b4a8d.tar.bz2
#362024 by neclimdul, yched, and bjaspan: Make hook_field_load() multiple like field_attach_load().
Diffstat (limited to 'modules/field/field.attach.inc')
-rw-r--r--modules/field/field.attach.inc234
1 files changed, 140 insertions, 94 deletions
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index a6fbdf0a9..92bcbb068 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -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;
@@ -194,6 +186,85 @@ function _field_invoke($op, $obj_type, &$object, &$a = NULL, &$b = NULL, $defaul
}
/**
+ * 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.
*/
function _field_invoke_default($op, $obj_type, &$object, &$a = NULL, &$b = NULL) {
@@ -201,6 +272,13 @@ function _field_invoke_default($op, $obj_type, &$object, &$a = NULL, &$b = NULL)
}
/**
+ * 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"
*
* The rest of the functions in this file are not in a group, but
@@ -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.