diff --git a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php index 8a9ce80aae48659f31ece4b46bac11363c6dbb73..8d86a6badc5bfded092236ae3d9c2b2f328acf32 100644 --- a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php +++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php @@ -7,9 +7,48 @@ namespace Drupal\Core\Entity\Display; +use Drupal\Core\Entity\ContentEntityInterface; + /** * Provides a common interface for entity view displays. */ interface EntityViewDisplayInterface extends EntityDisplayInterface { + /** + * Returns a renderable array for the components of an entity. + * + * See the buildMultiple() method for details. + * + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The entity being displayed. + * + * @return array + * A renderable array for the entity. + * + * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface::buildMultiple() + */ + public function build(ContentEntityInterface $entity); + + /** + * Returns a renderable array for the components of a set of entities. + * + * This only includes the components handled by the Display object, but + * excludes 'extra fields', that are typically rendered through specific, + * ad-hoc code in EntityViewBuilderInterface::buildContent() or in + * hook_entity_view() implementations. + * + * hook_entity_display_build_alter() is invoked on each entity, allowing 3rd + * party code to alter the render array. + * + * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities + * The entities being displayed. + * + * @return array + * A renderable array for the entities, indexed by the same keys as the + * $entities array parameter. + * + * @see hook_entity_display_build_alter() + */ + public function buildMultiple(array $entities); + } diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index e67657c4cdcf941a3f8ac15e0a84723de8e657ea..bffc73109f30415dbf321464d467e8679e80f79b 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -89,16 +89,18 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * {@inheritdoc} */ public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) { - field_attach_prepare_view($this->entityTypeId, $entities, $displays, $langcode); - - // Initialize the field item attributes for the fields set to be displayed. - foreach ($entities as $entity) { - // The entity can include fields that aren't displayed, and the display - // can include components that aren't fields, so we want to iterate the - // intersection of $entity->getProperties() and $display->getComponents(). - // However, the entity can have many more fields than are displayed, so we - // avoid the cost of calling $entity->getProperties() by iterating the - // intersection as follows. + $entities_by_bundle = array(); + foreach ($entities as $id => $entity) { + // Remove previously built content, if exists. + $entity->content = array( + '#view_mode' => $view_mode, + ); + // Initialize the field item attributes for the fields being displayed. + // The entity can include fields that are not displayed, and the display + // can include components that are not fields, so we want to act on the + // intersection. However, the entity can have many more fields than are + // displayed, so we avoid the cost of calling $entity->getProperties() + // by iterating the intersection as follows. foreach ($displays[$entity->bundle()]->getComponents() as $name => $options) { if ($entity->hasField($name)) { foreach ($entity->get($name) as $item) { @@ -106,16 +108,19 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang } } } + // Group the entities by bundle. + $entities_by_bundle[$entity->bundle()][$id] = $entity; } + // Invoke hook_entity_prepare_view(). module_invoke_all('entity_prepare_view', $this->entityTypeId, $entities, $displays, $view_mode); - foreach ($entities as $entity) { - // Remove previously built content, if exists. - $entity->content = array( - '#view_mode' => $view_mode, - ); - $entity->content += field_attach_view($entity, $displays[$entity->bundle()], $langcode); + // Let the displays build their render arrays. + foreach ($entities_by_bundle as $bundle => $bundle_entities) { + $build = $displays[$bundle]->buildMultiple($bundle_entities); + foreach ($bundle_entities as $id => $entity) { + $entity->content += $build[$id]; + } } } @@ -231,6 +236,9 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la $this->alterBuild($build[$key], $entity, $display, $entity_view_mode, $langcode); // Assign the weights configured in the display. + // @todo: Once https://drupal.org/node/1875974 provides the missing API, + // only do it for 'extra fields', since other components have been taken + // care of in EntityViewDisplay::buildMultiple(). foreach ($display->getComponents() as $name => $options) { if (isset($build[$key][$name])) { $build[$key][$name]['#weight'] = $options['weight']; diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php index 6cd09609fce381bea5ea954e8930adbe948c61a6..14e4ba0cf54d72b47fe80ce1d3d60efef62fc85f 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php @@ -15,9 +15,9 @@ interface EntityViewBuilderInterface { /** * Build the structured $content property on the entity. * - * @param array $entities - * The entities, implementing EntityInterface, whose content is being built. - * @param \Drupal\Core\Entity\EntityViewDisplayInterface[] $displays + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The entities whose content is being built. + * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays * The array of entity view displays holding the display options * configured for the entity components, keyed by bundle name. * @param string $view_mode diff --git a/core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php b/core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php index 1ef899eeea7fb9ba2bba5344cae79ffa89cd1de5..11f224622cc66d2ee62da4a03019f9fef99c13cf 100644 --- a/core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php +++ b/core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php @@ -12,9 +12,8 @@ /** * Defines a FieldFormatter annotation object. * - * Formatters handle the display of field values. Formatter hooks are typically - * called by the Field Attach API field_attach_prepare_view() and - * field_attach_view() functions. + * Formatters handle the display of field values. They are typically + * instantiated and invoked by an EntityDisplay object. * * Additional annotation keys for formatters can be defined in * hook_field_formatter_info_alter(). diff --git a/core/lib/Drupal/Core/Field/FormatterInterface.php b/core/lib/Drupal/Core/Field/FormatterInterface.php index ef6776d96d331bacb89ed904cde0a31524a0bc5b..fa633727024325804af06dc2ba70289d287dc408 100644 --- a/core/lib/Drupal/Core/Field/FormatterInterface.php +++ b/core/lib/Drupal/Core/Field/FormatterInterface.php @@ -48,22 +48,20 @@ public function settingsSummary(); * field that displays properties of the referenced entities such as name or * type. * - * This method operates on multiple entities. The $entities and $items - * parameters are arrays keyed by entity ID. For performance reasons, - * information for all involved entities should be loaded in a single query - * where possible. + * This method operates on multiple entities. The $entities_items parameter + * is an array keyed by entity ID. For performance reasons, information for + * all involved entities should be loaded in a single query where possible. * - * Changes or additions to field values are done by alterings the $items - * parameter by reference. + * Changes or additions to field values are done by directly altering the + * items. * - * @param array $entities_items - * Array of field values (Drupal\Core\Field\FieldItemListInterface), - * keyed by entity ID. + * @param \Drupal\Core\Field\FieldItemListInterface[] $entities_items + * Array of field values, keyed by entity ID. */ public function prepareView(array $entities_items); /** - * Builds a renderable array for one field on one entity instance. + * Builds a renderable array for a fully themed field. * * @param \Drupal\Core\Field\FieldItemListInterface $items * The field values to be rendered. @@ -81,7 +79,7 @@ public function view(FieldItemListInterface $items); * * @return array * A renderable array for $items, as an array of child elements keyed by - * numeric indexes starting from 0. + * consecutive numeric indexes starting from 0. */ public function viewElements(FieldItemListInterface $items); diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php index c2ec06801bcde5a47e0d5af27d2b79c86d944fae..4aeec859ee27846a407069c90c940ac2b2764248 100644 --- a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php +++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php @@ -2,11 +2,12 @@ /** * @file - * Contains \Drupal\datetime\Tests\DatetimeFieldTest. + * Contains \Drupal\datetime\Tests\DateTimeFieldTest. */ namespace Drupal\datetime\Tests; +use Drupal\entity\Entity\EntityViewDisplay; use Drupal\simpletest\WebTestBase; use Drupal\Core\Datetime\DrupalDateTime; @@ -455,10 +456,8 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) { entity_get_controller('entity_test')->resetCache(array($id)); } $entity = entity_load('entity_test', $id); - $display = entity_get_display('entity_test', $entity->bundle(), 'full'); - field_attach_prepare_view('entity_test', array($entity->id() => $entity), array($entity->bundle() => $display), $view_mode); - $entity->content = field_attach_view($entity, $display, $view_mode); - + $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); + $entity->content = $display->build($entity); $output = drupal_render($entity->content); $this->drupalSetContent($output); $this->verbose($output); diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php index a359150b32ceb722fba962190493ebe31a5b413c..6af964f802e871182c563e29b4e0007d9cb9b877 100644 --- a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php +++ b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php @@ -111,8 +111,8 @@ function testEmailField() { // Verify that a mailto link is displayed. $entity = entity_load('entity_test', $id); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - $entity->content = field_attach_view($entity, $display); - $this->drupalSetContent(drupal_render($entity->content)); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertLinkByHref('mailto:test@example.com'); } } diff --git a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php index b34259b243e5404cbf15f99f23ef99c781b0b12a..5ab421e479c9d1b9aef7e31e2ad44d23117b6997 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\entity\EntityDisplayBase; /** @@ -197,4 +198,62 @@ public function getRenderer($field_name) { return $formatter; } + /** + * {@inheritdoc} + */ + public function build(ContentEntityInterface $entity) { + $build = $this->buildMultiple(array($entity)); + return $build[0]; + } + + /** + * {@inheritdoc} + */ + public function buildMultiple(array $entities) { + $build = array(); + foreach ($entities as $key => $entity) { + $build[$key] = array(); + } + + // Run field formatters. + foreach ($this->getFieldDefinitions() as $field_name => $definition) { + if ($formatter = $this->getRenderer($field_name)) { + // Group items across all entities and pass them to the formatter's + // prepareView() method. + $grouped_items = array(); + foreach ($entities as $id => $entity) { + $items = $entity->get($field_name); + $items->filterEmptyItems(); + $grouped_items[$id] = $items; + } + $formatter->prepareView($grouped_items); + + // Then let the formatter build the output for each entity. + foreach ($entities as $key => $entity) { + $items = $entity->get($field_name); + $build[$key] += $formatter->view($items); + } + } + } + + foreach ($entities as $key => $entity) { + // Assign the configured weights. + foreach ($this->getComponents() as $name => $options) { + if (isset($build[$key][$name])) { + $build[$key][$name]['#weight'] = $options['weight']; + } + } + + // Let other modules alter the renderable array. + $context = array( + 'entity' => $entity, + 'view_mode' => $this->originalMode, + 'display' => $this, + ); + \Drupal::moduleHandler()->alter('entity_display_build', $build[$key], $context); + } + + return $build; + } + } diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index a85300f79e543f9c9b987f4cc0831f72d5eddcff..e50152bb0a8be8a6d4f4856e23c295009d0c7d8c 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -19,7 +19,7 @@ * should expose them using this hook. The user-defined settings (weight, * visible) are automatically applied on rendered forms and displayed entities * in a #pre_render callback added by field_attach_form() and - * field_attach_view(). + * EntityViewBuilder::viewMultiple(). * * @see hook_field_extra_fields_alter() * @@ -357,46 +357,6 @@ function hook_field_attach_extract_form_values(\Drupal\Core\Entity\EntityInterfa } } -/** - * Perform alterations on field_attach_view() or field_view_field(). - * - * This hook is invoked after the field module has performed the operation. - * - * @param $output - * The structured content array tree for all of the entity's fields. - * @param $context - * An associative array containing: - * - entity: The entity with fields to render. - * - view_mode: View mode; for example, 'full' or 'teaser'. - * - display_options: Either a view mode string or an array of display - * options. If this hook is being invoked from field_attach_view(), the - * 'display_options' element is set to the view mode string. If this hook - * is being invoked from field_view_field(), this element is set to the - * $display_options argument and the view_mode element is set to '_custom'. - * See field_view_field() for more information on what its $display_options - * argument contains. - * - langcode: The language code used for rendering. - * - * @deprecated as of Drupal 8.0. Use the entity system instead. - */ -function hook_field_attach_view_alter(&$output, $context) { - // Append RDF term mappings on displayed taxonomy links. - foreach (element_children($output) as $field_name) { - $element = &$output[$field_name]; - if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') { - foreach ($element['#items'] as $delta => $item) { - $term = $item->entity; - if (!empty($term->rdf_mapping['rdftype'])) { - $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; - } - if (!empty($term->rdf_mapping['name']['predicates'])) { - $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; - } - } - } - } -} - /** * @} End of "addtogroup field_attach". */ diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc index d471223e106f1796111144821c42bb9953237196..59c6deb6b3363a4b838fec553d1ad5ee98833967 100644 --- a/core/modules/field/field.attach.inc +++ b/core/modules/field/field.attach.inc @@ -7,7 +7,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\entity\Entity\EntityFormDisplay; /** * @defgroup field_attach Field Attach API @@ -47,12 +46,6 @@ * exposes a single bundle (all entities of this type have the same collection * of fields). This is the case for the 'user' entity type. * - * Most Field Attach API functions define a corresponding hook function that - * allows any module to act on Field Attach operations for any entity after the - * operation is complete, and access or modify all the field, form, or display - * data for that entity and operation. For example, field_attach_view() invokes - * hook_field_attach_view_alter(). - * * @link field_language Field language API @endlink provides information about * the structure of field objects. * @@ -119,96 +112,6 @@ function field_invoke_method($method, $target_function, EntityInterface $entity, return $return; } -/** - * Invokes a method across fields on multiple entities. - * - * @param string $method - * The name of the method to invoke. - * @param callable $target_function - * A function that receives a FieldDefinitionInterface object and a bundle - * name and returns the object on which the method should be invoked. - * @param \Drupal\Core\Entity\EntityInterface[] $entities - * An array of entities, keyed by entity ID. - * @param mixed $a - * (optional) A parameter for the invoked method. Defaults to NULL. - * @param mixed $b - * (optional) A parameter for the invoked method. Defaults to NULL. - * @param $options - * (optional) An associative array of additional options, with the following - * keys: - * - field_name: The name of the field whose operation should be invoked. By - * default, the operation is invoked on all the fields in the entity's - * bundle. - * - * @return array - * An array of returned values keyed by entity ID. - * - * @see field_invoke_method() - */ -function field_invoke_method_multiple($method, $target_function, array $entities, &$a = NULL, &$b = NULL, array $options = array()) { - $grouped_items = array(); - $grouped_targets = array(); - $return = array(); - - // Go through the entities and collect the instances on which the method - // should be called. - foreach ($entities as $entity) { - $entity_type = $entity->getEntityTypeId(); - $bundle = $entity->bundle(); - $id = $entity->id(); - - // Determine the list of fields to iterate on. - $field_definitions = _field_invoke_get_field_definitions($entity_type, $bundle, $options); - - foreach ($field_definitions as $field_definition) { - $field_name = $field_definition->getName(); - $group_key = "$bundle:$field_name"; - - // Let the closure determine the target object on which the method should - // be called. - if (empty($grouped_targets[$group_key])) { - $target = call_user_func($target_function, $field_definition, $bundle); - if (method_exists($target, $method)) { - $grouped_targets[$group_key] = $target; - } - else { - $grouped_targets[$group_key] = FALSE; - } - } - - // If there is a target, group the field items. - if ($grouped_targets[$group_key]) { - $items = $entity->get($field_name); - $items->filterEmptyItems(); - $grouped_items[$group_key][$id] = $items; - } - } - // Initialize the return value for each entity. - $return[$id] = array(); - } - - // For each field, invoke the method and collect results. - foreach ($grouped_items as $key => $entities_items) { - $results = $grouped_targets[$key]->$method($entities_items, $a, $b); - - if (isset($results)) { - // Collect results by entity. - // For hooks with array results, we merge results together. - // For hooks with scalar results, we collect results in an array. - foreach ($results as $id => $result) { - if (is_array($result)) { - $return[$id] = array_merge($return[$id], $result); - } - else { - $return[$id][] = $result; - } - } - } - } - - return $return; -} - /** * Retrieves a list of field definitions to operate on. * diff --git a/core/modules/field/field.deprecated.inc b/core/modules/field/field.deprecated.inc index 1e63781743b1f9832dedfe730aa8b6ac6b37af91..6bdc8b2e037ceca5505007067a521288156564ab 100644 --- a/core/modules/field/field.deprecated.inc +++ b/core/modules/field/field.deprecated.inc @@ -387,111 +387,3 @@ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form $function($entity, $form, $form_state); } } - -/** - * Prepares field data prior to display. - * - * This function lets field types and formatters load additional data needed for - * display that is not automatically loaded during entity loading. It accepts an - * array of entities to allow query optimization when displaying lists of - * entities. - * - * field_attach_prepare_view() and field_attach_view() are two halves of the - * same operation. It is safe to call field_attach_prepare_view() multiple times - * on the same entity before calling field_attach_view() on it, but calling any - * Field API operation on an entity between passing that entity to these two - * functions may yield incorrect results. - * - * @param $entity_type - * The type of entities in $entities; e.g. 'node' or 'user'. - * @param array $entities - * An array of entities, keyed by entity ID. - * @param array $displays - * An array of entity display objects, keyed by bundle name. - * @param $langcode - * (Optional) The language the field values are to be shown in. If no language - * is provided the current language is used. - * - * @deprecated as of Drupal 8.0. Use the entity system instead. - */ -function field_attach_prepare_view($entity_type, array $entities, array $displays, $langcode = NULL) { - // To ensure hooks are only run once per entity, only process items without - // the _field_view_prepared flag. - // @todo: resolve this more generally for both entity and field level hooks. - $prepare = array(); - foreach ($entities as $id => $entity) { - if (empty($entity->_field_view_prepared)) { - // Add this entity to the items to be prepared. - $prepare[$id] = $entity; - - // Mark this item as prepared. - $entity->_field_view_prepared = TRUE; - } - } - - // Then let the formatters do their own specific massaging. For each - // instance, call the prepareView() method on the formatter object handed by - // the entity display. - $target_function = function (FieldDefinitionInterface $field_definition, $bundle) use ($displays) { - if (isset($displays[$bundle])) { - return $displays[$bundle]->getRenderer($field_definition->getName()); - } - }; - $null = NULL; - field_invoke_method_multiple('prepareView', $target_function, $prepare, $null, $null); -} - -/** - * Returns a renderable array for the fields on an entity. - * - * Each field is displayed according to the display options specified in the - * $display parameter for the given view mode. - * - * field_attach_prepare_view() and field_attach_view() are two halves of the - * same operation. It is safe to call field_attach_prepare_view() multiple times - * on the same entity before calling field_attach_view() on it, but calling any - * Field API operation on an entity between passing that entity to these two - * functions may yield incorrect results. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity with fields to render. - * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display - * The entity display object. - * @param $langcode - * The language the field values are to be shown in. If no language is - * provided the current language is used. - * @param array $options - * An associative array of additional options. See field_invoke_method() for - * details. - * - * @return array - * A renderable array for the field values. - * - * @deprecated as of Drupal 8.0. Use the entity system instead. - */ -function field_attach_view(EntityInterface $entity, EntityViewDisplayInterface $display, $langcode = NULL, array $options = array()) { - // For each field, call the view() method on the formatter object handed - // by the entity display. - $target_function = function (FieldDefinitionInterface $field_definition) use ($display) { - return $display->getRenderer($field_definition->getName()); - }; - $null = NULL; - $output = field_invoke_method('view', $target_function, $entity, $null, $null, $options); - - // Let other modules alter the renderable array. - $view_mode = $display->originalMode; - $context = array( - 'entity' => $entity, - 'view_mode' => $view_mode, - 'display_options' => $view_mode, - 'langcode' => $langcode, - ); - drupal_alter('field_attach_view', $output, $context); - - // Reset the _field_view_prepared flag set in field_attach_prepare_view(), - // in case the same entity is displayed with different settings later in - // the request. - unset($entity->_field_view_prepared); - - return $output; -} diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 883814d2a0c2a990629c70c246f24789d9054370..6a8b941125447bc85e32b34cf043f41a03e657ba 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -5,7 +5,6 @@ */ use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Template\Attribute; use Drupal\entity\Entity\EntityViewDisplay; @@ -325,7 +324,7 @@ function _field_filter_xss_display_allowed_tags() { /** * Returns a renderable array for a single field value. * - * @param \Drupal\Core\Entity\EntityInterface $entity + * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity containing the field to display. Must at least contain the ID * key and the field data to display. * @param $field_name @@ -342,10 +341,10 @@ function _field_filter_xss_display_allowed_tags() { * @return * A renderable array for the field value. */ -function field_view_value(EntityInterface $entity, $field_name, $item, $display = array(), $langcode = NULL) { +function field_view_value(ContentEntityInterface $entity, $field_name, $item, $display = array(), $langcode = NULL) { $output = array(); - if ($field = field_info_field($entity->getEntityTypeId(), $field_name)) { + if ($entity->hasField($field_name)) { // Clone the entity since we are going to modify field values. $clone = clone $entity; @@ -376,8 +375,8 @@ function field_view_value(EntityInterface $entity, $field_name, $item, $display * isolated field. * - Do not use inside node (or any other entity) templates; use * render($content[FIELD_NAME]) instead. - * - Do not use to display all fields in an entity; use - * field_attach_prepare_view() and field_attach_view() instead. + * - Do not use to display all fields in an entity; use EntityDisplay::build() + * instead. * - The field_view_value() function can be used to output a single formatted * field value, without label or wrapping field markup. * @@ -424,49 +423,31 @@ function field_view_field(ContentEntityInterface $entity, $field_name, $display_ if (!$entity->hasField($field_name)) { return $output; } - $field_definition = $entity->get($field_name)->getFieldDefinition(); - // Get the formatter object. + // Get the display object. if (is_string($display_options)) { $view_mode = $display_options; - $formatter = EntityViewDisplay::collectRenderDisplay($entity, $view_mode)->getRenderer($field_name); + $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); + foreach ($entity as $name => $items) { + if ($name != $field_name) { + $display->removeComponent($name); + } + } } else { $view_mode = '_custom'; - // hook_field_attach_display_alter() needs to receive the 'prepared' - // $display_options, so we cannot let preparation happen internally. - $formatter_manager = Drupal::service('plugin.manager.field.formatter'); - $display_options = $formatter_manager->prepareConfiguration($field_definition->getType(), $display_options); - $formatter = $formatter_manager->getInstance(array( - 'field_definition' => $field_definition, - 'view_mode' => $view_mode, - 'prepare' => FALSE, - 'configuration' => $display_options, + $display = entity_create('entity_view_display', array( + 'targetEntityType' => $entity->getEntityTypeId(), + 'bundle' => $entity->bundle(), + 'mode' => $view_mode, + 'status' => TRUE, )); + $display->setComponent($field_name, $display_options); } - if ($formatter) { - // Apply language fallback. - $entity = \Drupal::entityManager()->getTranslationFromContext($entity, $langcode); - $items = $entity->get($field_name); - - // Run the formatter. - $formatter->prepareView(array($entity->id() => $items)); - $result = $formatter->view($items); - - // Invoke hook_field_attach_view_alter() to let other modules alter the - // renderable array, as in a full field_attach_view() execution. - $context = array( - 'entity' => $entity, - 'view_mode' => $view_mode, - 'display_options' => $display_options, - 'langcode' => $entity->language()->id, - ); - drupal_alter('field_attach_view', $result, $context); - - if (isset($result[$field_name])) { - $output = $result[$field_name]; - } + $build = $display->build($entity); + if (isset($build[$field_name])) { + $output = $build[$field_name]; } return $output; diff --git a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php index 750eee6be04c732948be4a3cc47f66ce61a3cf08..00ebc40f5b0a8f4504ec8cbf7ff55358d4488f21 100644 --- a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php @@ -142,8 +142,8 @@ function testFieldViewField() { $this->content = drupal_render($output); $setting = $display['settings']['test_formatter_setting_multiple']; $this->assertNoText($this->label, 'Label was not displayed.'); - $this->assertText('field_test_field_attach_view_alter', 'Alter fired, display passed.'); - $this->assertText('field language is ' . Language::LANGCODE_NOT_SPECIFIED, 'Language is placed onto the context.'); + $this->assertText('field_test_entity_display_build_alter', 'Alter fired, display passed.'); + $this->assertText('entity language is ' . Language::LANGCODE_NOT_SPECIFIED, 'Language is placed onto the context.'); $array = array(); foreach ($this->values as $delta => $value) { $array[] = $delta . ':' . $value['value']; @@ -163,7 +163,7 @@ function testFieldViewField() { $this->content = $view; $setting = $display['settings']['test_formatter_setting_additional']; $this->assertNoText($this->label, 'Label was not displayed.'); - $this->assertNoText('field_test_field_attach_view_alter', 'Alter not fired.'); + $this->assertNoText('field_test_entity_display_build_alter', 'Alter not fired.'); foreach ($this->values as $delta => $value) { $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta))); } diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php index ba6dd72a00c674e7980c212a730ed685f2220c64..b4ce690ca74331ba5a8f4c127e85ed3f80c45d3c 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php @@ -42,9 +42,9 @@ public function setUp() { } /** - * Test field_attach_view() and field_attach_prepare_view(). + * Test rendering fields with EntityDisplay build(). */ - function testFieldAttachView() { + function testEntityDisplayBuild() { $this->createFieldWithInstance('_2'); $entity_type = 'entity_test'; @@ -59,7 +59,6 @@ function testFieldAttachView() { // Simple formatter, label displayed. $entity = clone($entity_init); $display = entity_get_display($entity_type, $entity->bundle(), 'full'); - $displays = array($entity->bundle() => $display); $formatter_setting = $this->randomName(); $display_options = array( @@ -82,18 +81,14 @@ function testFieldAttachView() { $display->setComponent($this->field_2->getName(), $display_options_2); // View all fields. - field_attach_prepare_view($entity_type, array($entity->id() => $entity), $displays); - $content = field_attach_view($entity, $display); - $output = drupal_render($content); - $this->content = $output; + $content = $display->build($entity); + $this->content = drupal_render($content); $this->assertRaw($this->instance->getLabel(), "First field's label is displayed."); foreach ($values as $delta => $value) { - $this->content = $output; $this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied."); } $this->assertRaw($this->instance_2->getLabel(), "Second field's label is displayed."); foreach ($values_2 as $delta => $value) { - $this->content = $output; $this->assertRaw("$formatter_setting_2|{$value['value']}", "Value $delta is displayed, formatter settings are applied."); } @@ -101,19 +96,15 @@ function testFieldAttachView() { $entity = clone($entity_init); $display_options['label'] = 'hidden'; $display->setComponent($this->field->getName(), $display_options); - field_attach_prepare_view($entity_type, array($entity->id() => $entity), $displays); - $entity->content = field_attach_view($entity, $display); - $output = drupal_render($entity->content); - $this->content = $output; + $content = $display->build($entity); + $this->content = drupal_render($content); $this->assertNoRaw($this->instance->getLabel(), "Hidden label: label is not displayed."); // Field hidden. $entity = clone($entity_init); $display->removeComponent($this->field->getName()); - field_attach_prepare_view($entity_type, array($entity->id() => $entity), $displays); - $entity->content = field_attach_view($entity, $display); - $output = drupal_render($entity->content); - $this->content = $output; + $content = $display->build($entity); + $this->content = drupal_render($content); $this->assertNoRaw($this->instance->getLabel(), "Hidden field: label is not displayed."); foreach ($values as $delta => $value) { $this->assertNoRaw("$formatter_setting|{$value['value']}", "Hidden field: value $delta is not displayed."); @@ -129,14 +120,12 @@ function testFieldAttachView() { 'test_formatter_setting_multiple' => $formatter_setting, ), )); - field_attach_prepare_view($entity_type, array($entity->id() => $entity), $displays); - $entity->content = field_attach_view($entity, $display); - $output = drupal_render($entity->content); + $content = $display->build($entity); + $this->content = drupal_render($content); $expected_output = $formatter_setting; foreach ($values as $delta => $value) { $expected_output .= "|$delta:{$value['value']}"; } - $this->content = $output; $this->assertRaw($expected_output, "Multiple formatter: all values are displayed, formatter settings are applied."); // Test a formatter that uses hook_field_formatter_prepare_view(). @@ -149,10 +138,8 @@ function testFieldAttachView() { 'test_formatter_setting_additional' => $formatter_setting, ), )); - field_attach_prepare_view($entity_type, array($entity->id() => $entity), $displays); - $entity->content = field_attach_view($entity, $display); - $output = drupal_render($entity->content); - $this->content = $output; + $content = $display->build($entity); + $this->content = drupal_render($content); foreach ($values as $delta => $value) { $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1); $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied."); @@ -163,58 +150,27 @@ function testFieldAttachView() { } /** - * Tests the 'multiple entity' behavior of field_attach_prepare_view(). + * Tests rendering fields with EntityDisplay::buildMultiple(). */ - function testFieldAttachPrepareViewMultiple() { - $entity_type = 'entity_test'; - - // Set the instance to be hidden. + function testEntityDisplayViewMultiple() { + // Use a formatter that has a prepareView() step. $display = entity_get_display('entity_test', 'entity_test', 'full') - ->removeComponent($this->field->getName()); - - // Set up a second instance on another bundle, with a formatter that uses - // hook_field_formatter_prepare_view(). - entity_test_create_bundle('test_bundle_2'); - $formatter_setting = $this->randomName(); - $instance_definition = $this->instance_definition; - $instance_definition['bundle'] = 'test_bundle_2'; - $this->instance2 = entity_create('field_instance', $instance_definition); - $this->instance2->save(); - - $display_2 = entity_get_display('entity_test', 'test_bundle_2', 'full') - ->setComponent($this->field->getName(), array( + ->setComponent($this->field_name, array( 'type' => 'field_test_with_prepare_view', - 'settings' => array( - 'test_formatter_setting_additional' => $formatter_setting, - ), )); - $displays = array('entity_test' => $display, 'test_bundle_2' => $display_2); - - // Create one entity in each bundle. - $entity1_init = entity_create('entity_test', array('id' => 1, 'type' => 'entity_test')); - $values1 = $this->_generateTestFieldValues($this->field->getCardinality()); - $entity1_init->{$this->field_name}->setValue($values1); - - $entity2_init = entity_create('entity_test', array('id' => 2, 'type' => 'test_bundle_2')); - $values2 = $this->_generateTestFieldValues($this->field->getCardinality()); - $entity2_init->{$this->field_name}->setValue($values2); - - // Run prepare_view, and check that the entities come out as expected. - $entity1 = clone($entity1_init); - $entity2 = clone($entity2_init); - $entities = array($entity1->id() => $entity1, $entity2->id() => $entity2); - field_attach_prepare_view($entity_type, $entities, $displays); - $this->assertFalse(isset($entity1->{$this->field_name}->additional_formatter_value), 'Entity 1 did not run through the prepare_view hook.'); - $this->assertTrue(isset($entity2->{$this->field_name}->additional_formatter_value), 'Entity 2 ran through the prepare_view hook.'); - - // Same thing, reversed order. - $entity1 = clone($entity1_init); - $entity2 = clone($entity2_init); - $entities = array($entity1->id() => $entity1, $entity2->id() => $entity2); - field_attach_prepare_view($entity_type, $entities, $displays); - $this->assertFalse(isset($entity1->{$this->field_name}->additional_formatter_value), 'Entity 1 did not run through the prepare_view hook.'); - $this->assertTrue(isset($entity2->{$this->field_name}->additional_formatter_value), 'Entity 2 ran through the prepare_view hook.'); + // Create two entities. + $entity1 = entity_create('entity_test', array('id' => 1, 'type' => 'entity_test')); + $entity1->{$this->field_name}->setValue($this->_generateTestFieldValues(1)); + $entity2 = entity_create('entity_test', array('id' => 2, 'type' => 'test_bundle')); + $entity2->{$this->field_name}->setValue($this->_generateTestFieldValues(1)); + + // Run buildMultiple(), and check that the entities come out as expected. + $display->buildMultiple(array($entity1, $entity2)); + $item1 = $entity1->{$this->field_name}[0]; + $this->assertEqual($item1->additional_formatter_value, $item1->value + 1, 'Entity 1 ran through the prepareView() formatter method.'); + $item2 = $entity2->{$this->field_name}[0]; + $this->assertEqual($item2->additional_formatter_value, $item2->value + 1, 'Entity 2 ran through the prepareView() formatter method.'); } /** diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module index 8215416b7873b055a2299a1ad5c59b80bbd89f18..a524bb9f0abf98a4510eb0a473e9649ba1715e35 100644 --- a/core/modules/field/tests/modules/field_test/field_test.module +++ b/core/modules/field/tests/modules/field_test/field_test.module @@ -112,15 +112,16 @@ function field_test_field_entity_create(FieldInterface $field) { } /** - * Implements hook_field_attach_view_alter(). + * Implements hook_entity_display_build_alter(). */ -function field_test_field_attach_view_alter(&$output, $context) { - if (!empty($context['display_options']['settings']['alter'])) { - $output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter'); +function field_test_entity_display_build_alter(&$output, $context) { + $display_options = $context['display']->getComponent('test_field'); + if (isset($display_options['settings']['alter'])) { + $output['test_field'][] = array('#markup' => 'field_test_entity_display_build_alter'); } if (isset($output['test_field'])) { - $output['test_field'][] = array('#markup' => 'field language is ' . $context['langcode']); + $output['test_field'][] = array('#markup' => 'entity language is ' . $context['entity']->language()->id); } } diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php index bf61616dc2b783ec0ba52990db29032e450b30c7..fa86ecbc8e89e3cf25935c5f3c35b9149536b06f 100644 --- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php +++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php @@ -516,10 +516,8 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) { } $entity = entity_load('entity_test', $id); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode); - field_attach_prepare_view('entity_test', array($entity->id() => $entity), array($entity->bundle() => $display)); - $entity->content = field_attach_view($entity, $display); - - $output = drupal_render($entity->content); + $content = $display->build($entity); + $output = drupal_render($content); $this->drupalSetContent($output); $this->verbose($output); } diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index ffcd635211739acd26b57e697822d2c0ad609f28..ab7811e64940672b439961a94dc52de6fdfe7def 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -61,9 +61,8 @@ * - hook_node_load() (all) * - Viewing a single node (calling node_view() - note that the input to * node_view() is a loaded node, so the Loading steps above are already done): - * - field_attach_prepare_view() * - hook_entity_prepare_view() (all) - * - field_attach_view() + * - hook_entity_display_build_alter() (all) * - hook_node_view() (all) * - hook_entity_view() (all) * - hook_node_view_alter() (all) @@ -71,9 +70,8 @@ * - Viewing multiple nodes (calling node_view_multiple() - note that the input * to node_view_multiple() is a set of loaded nodes, so the Loading steps * above are already done): - * - field_attach_prepare_view() * - hook_entity_prepare_view() (all) - * - field_attach_view() + * - hook_entity_display_build_alter() (all) * - hook_node_view() (all) * - hook_entity_view() (all) * - hook_node_view_alter() (all) diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index 928d292bed6bca479752ce022af271c7233ab239..852f7f9e7779ec2f505a29a190ce41447f7caae2 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -514,7 +514,7 @@ function hook_entity_view_alter(&$build, Drupal\Core\Entity\EntityInterface $ent * The type of entities being viewed (i.e. node, user, comment). * @param array $entities * The entities keyed by entity ID. - * @param \Drupal\Core\Entity\EntityViewDisplayInterface[] $displays + * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays * The array of entity view displays holding the display options configured * for the entity components, keyed by bundle name. * @param string $view_mode @@ -583,6 +583,35 @@ function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDi } } +/** + * Alter the render array generated by an EntityDisplay for an entity. + * + * @param array $build + * The renderable array generated by the EntityDisplay. + * @param array $context + * An associative array containing: + * - entity: The entity being rendered. + * - view_mode: The view mode; for example, 'full' or 'teaser'. + * - display: The EntityDisplay holding the display options. + */ +function hook_entity_display_build_alter(&$build, $context) { + // Append RDF term mappings on displayed taxonomy links. + foreach (element_children($build) as $field_name) { + $element = &$build[$field_name]; + if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') { + foreach ($element['#items'] as $delta => $item) { + $term = $item->entity; + if (!empty($term->rdf_mapping['rdftype'])) { + $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; + } + if (!empty($term->rdf_mapping['name']['predicates'])) { + $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + } + } + } + } +} + /** * Acts on an entity object about to be shown on an entity form. * diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php index 9a2aa4396a2f9915604567d20a6bca7cc2eec5ab..098286468ddcf8b48f876d9cc6b93ec1edd76537 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php @@ -100,11 +100,9 @@ function testTaxonomyTermFieldMultipleVocabularies() { // Render the entity. $entity = entity_load('entity_test', $id); - $entities = array($id => $entity); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - field_attach_prepare_view('entity_test', $entities, array($entity->bundle() => $display)); - $entity->content = field_attach_view($entity, $display); - $this->content = drupal_render($entity->content); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertText($term1->label(), 'Term 1 name is displayed.'); $this->assertText($term2->label(), 'Term 2 name is displayed.'); @@ -113,12 +111,9 @@ function testTaxonomyTermFieldMultipleVocabularies() { // Re-render the content. $entity = entity_load('entity_test', $id); - $entities = array($id => $entity); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - field_attach_prepare_view('entity_test', $entities, array($entity->bundle() => $display)); - $entity->content = field_attach_view($entity, $display); - $this->plainTextContent = FALSE; - $this->content = drupal_render($entity->content); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); // Term 1 should still be displayed; term 2 should not be. $this->assertText($term1->label(), 'Term 1 name is displayed.'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php index 13e4fd3e5df018edc5b111d41a09679643e0d82c..ce24d95e7aea665016b6944ec53ada925e7b63d2 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php @@ -117,11 +117,9 @@ function testTaxonomyTermFieldWidgets() { // Display the object. $entity = entity_load('entity_test', $id); - $entities = array($id => $entity); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - field_attach_prepare_view('entity_test', $entities, array($entity->bundle() => $display)); - $entity->content = field_attach_view($entity, $display); - $this->content = drupal_render($entity->content); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertText($term->label(), 'Term label is displayed.'); // Delete the vocabulary and verify that the widget is gone. diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index 82b20437092204a56ec10fe15fa4c487306db7a9..2890f27bea2174225ea72bb46049457133023818 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -7,9 +7,9 @@ namespace Drupal\text\Tests\Formatter; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Language\Language; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; +use Drupal\Core\Language\Language; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -116,13 +116,13 @@ protected function createEntity($values = array()) { /** * Renders fields of a given entity with a given display. * - * @param \Drupal\Core\Entity\EntityInterface $entity + * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity object with attached fields to render. * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display * The display to render the fields in. */ - protected function renderEntityFields(EntityInterface $entity, EntityViewDisplayInterface $display) { - $content = field_attach_view($entity, $display); + protected function renderEntityFields(ContentEntityInterface $entity, EntityViewDisplayInterface $display) { + $content = $display->build($entity); $this->content = drupal_render($content); return $this->content; } diff --git a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php index 8ba14cab7a67a9ed5151ecabfc15b879de406b91..e68a752fca096774c3cf1149812478a24decf69e 100644 --- a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php @@ -139,8 +139,8 @@ function _testTextfieldWidgets($field_type, $widget_type) { // Display the entity. $entity = entity_load('entity_test', $id); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - $entity->content = field_attach_view($entity, $display); - $this->drupalSetContent(drupal_render($entity->content)); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertText($value, 'Filtered tags are not displayed'); } @@ -212,8 +212,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { // Display the entity. $entity = entity_load('entity_test', $id); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - $entity->content = field_attach_view($entity, $display); - $this->content = drupal_render($entity->content); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertNoRaw($value, 'HTML tags are not displayed.'); $this->assertRaw(check_plain($value), 'Escaped HTML is displayed correctly.'); @@ -254,8 +254,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { $this->container->get('entity.manager')->getStorageController('entity_test')->resetCache(array($id)); $entity = entity_load('entity_test', $id); $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full'); - $entity->content = field_attach_view($entity, $display); - $this->content = drupal_render($entity->content); + $content = $display->build($entity); + $this->drupalSetContent(drupal_render($content)); $this->assertRaw($value, 'Value is displayed unfiltered'); } }