summaryrefslogtreecommitdiff
path: root/modules/field
diff options
context:
space:
mode:
Diffstat (limited to 'modules/field')
-rw-r--r--modules/field/field.api.php70
-rw-r--r--modules/field/field.attach.inc7
-rw-r--r--modules/field/field.default.inc24
-rw-r--r--modules/field/field.test39
4 files changed, 134 insertions, 6 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php
index ee80409a8..3728ba226 100644
--- a/modules/field/field.api.php
+++ b/modules/field/field.api.php
@@ -248,8 +248,11 @@ function hook_field_schema($field) {
* 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.
+ * Note that the changes made to the field values get cached by the field cache
+ * for subsequent loads. You should never use this hook to load fieldable
+ * entities, since this is likely to cause infinite recursions when
+ * hook_field_load() is run on those as well. Use
+ * hook_field_formatter_prepare_view() instead.
*
* @param $obj_type
* The type of $object.
@@ -826,6 +829,34 @@ function theme_field_formatter_FORMATTER_MULTIPLE($variables) {
}
/**
+ * Allow formatters to load information for multiple objects.
+ *
+ * This should be used when a formatter needs to load additional information
+ * from the database in order to render a field, for example a reference field
+ * which displays properties of the referenced objects such as name or type.
+ *
+ * @param $obj_type
+ * The type of $object.
+ * @param $objects
+ * Array of objects being displayed, keyed by object id.
+ * @param $field
+ * The field structure for the operation.
+ * @param $instances
+ * Array of instance structures for $field for each object, keyed by object id.
+ * @param $langcode
+ * The language the field values are to be shown in. If no language is
+ * provided the current language is used.
+ * @param $items
+ * Array of field values for the objects, keyed by object id.
+ * @return
+ * Changes or additions to field values are done by altering the $items
+ * parameter by reference.
+ */
+function hook_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) {
+
+}
+
+/**
* @} End of "ingroup field_type"
*/
@@ -842,6 +873,41 @@ function theme_field_formatter_FORMATTER_MULTIPLE($variables) {
* See field_attach_form() for details and arguments.
*/
function hook_field_attach_form($obj_type, $object, &$form, &$form_state, $langcode) {
+ $tids = array();
+
+ // Collect every possible term attached to any of the fieldable entities.
+ foreach ($objects as $id => $object) {
+ foreach ($items[$id] as $delta => $item) {
+ // Force the array key to prevent duplicates.
+ $tids[$item['value']] = $item['value'];
+ }
+ }
+ if ($tids) {
+ $terms = array();
+
+ // Avoid calling taxonomy_term_load_multiple because it could lead to
+ // circular references.
+ $query = db_select('taxonomy_term_data', 't');
+ $query->fields('t');
+ $query->condition('t.tid', $tids, 'IN');
+ $query->addTag('term_access');
+ $terms = $query->execute()->fetchAllAssoc('tid');
+
+ // Iterate through the fieldable entities again to attach the loaded term data.
+ foreach ($objects as $id => $object) {
+ foreach ($items[$id] as $delta => $item) {
+ // Check whether the taxonomy term field instance value could be loaded.
+ if (isset($terms[$item['value']])) {
+ // Replace the instance value with the term data.
+ $items[$id][$delta]['taxonomy_term'] = $terms[$item['value']];
+ }
+ // Otherwise, unset the instance value, since the term does not exist.
+ else {
+ unset($items[$id][$delta]);
+ }
+ }
+ }
+ }
}
/**
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index 2bf38e457..3630ea40d 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -1110,6 +1110,13 @@ function field_attach_query_revisions($field_id, $conditions, $count, &$cursor =
}
/**
+ * Allow formatters to act on fieldable objects prior to rendering.
+ */
+function field_attach_prepare_view($obj_type, $objects, $build_mode = 'full') {
+ _field_invoke_multiple_default('prepare_view', $obj_type, $objects, $build_mode);
+}
+
+/**
* Generate and return a structured content array tree suitable for
* drupal_render() for all of the fields on an object. The format of
* each field's rendered content depends on the display formatter and
diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc
index 31d5732c4..fc7c40e40 100644
--- a/modules/field/field.default.inc
+++ b/modules/field/field.default.inc
@@ -54,6 +54,30 @@ function field_default_insert($obj_type, $object, $field, $instance, $langcode,
}
/**
+ * Invoke hook_field_formatter_prepare_view() on the relavant formatters.
+ */
+function field_default_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $options, $build_mode) {
+ // Group objects, instances and items by formatter module.
+ $modules = array();
+ foreach ($instances as $id => $instance) {
+ $module = $instance['display'][$build_mode]['module'];
+ $modules[$module] = $module;
+ $grouped_objects[$module][$id] = $objects[$id];
+ $grouped_instances[$module][$id] = $instance;
+ // hook_field_formatter_prepare_view() alters $items by reference.
+ $grouped_items[$module][$id] = &$items[$id];
+ }
+
+ foreach ($modules as $module) {
+ // Invoke hook_field_formatter_prepare_view().
+ $function = $module . '_field_formatter_prepare_view';
+ if (function_exists($function)) {
+ $function($obj_type, $grouped_objects[$module], $field, $grouped_instances[$module], $langcode, $grouped_items[$module], $build_mode);
+ }
+ }
+}
+
+/**
* Default field 'view' operation.
*
* @see field_attach_view()
diff --git a/modules/field/field.test b/modules/field/field.test
index 999c74ffa..86983e35b 100644
--- a/modules/field/field.test
+++ b/modules/field/field.test
@@ -786,18 +786,19 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
/**
- * Test field_attach_views() and field_attach_preprocess().
+ * Test field_attach_view() and field_atach_prepare_view().
*/
- function testFieldAttachViewAndPreprocess() {
+ function testFieldAttachView() {
$entity_type = 'test_entity';
- $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $entity_init = field_test_create_stub_entity();
$langcode = FIELD_LANGUAGE_NONE;
// Populate values to be displayed.
$values = $this->_generateTestFieldValues($this->field['cardinality']);
- $entity->{$this->field_name}[$langcode] = $values;
+ $entity_init->{$this->field_name}[$langcode] = $values;
// Simple formatter, label displayed.
+ $entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
@@ -809,6 +810,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
+ field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
@@ -819,14 +821,17 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
// Label hidden.
+ $entity = clone($entity_init);
$this->instance['display']['full']['label'] = 'hidden';
field_update_instance($this->instance);
+ field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
// Field hidden.
+ $entity = clone($entity_init);
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
@@ -834,6 +839,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
+ field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
@@ -843,6 +849,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
// Multiple formatter.
+ $entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
@@ -854,6 +861,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
+ field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$display = $formatter_setting;
@@ -863,6 +871,29 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$this->content = $output;
$this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
+ // Test a formatter that uses hook_field_formatter_prepare_view()..
+ $entity = clone($entity_init);
+ $formatter_setting = $this->randomName();
+ $this->instance['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'field_test_needs_additional_data',
+ 'settings' => array(
+ 'test_formatter_setting_additional' => $formatter_setting,
+ )
+ ),
+ );
+ field_update_instance($this->instance);
+ field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
+ $entity->content = field_attach_view($entity_type, $entity);
+ $output = drupal_render($entity->content);
+ $this->content = $output;
+ foreach ($values as $delta => $value) {
+ $this->content = $output;
+ $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
+ $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
+ }
+
// TODO:
// - check display order with several fields