Issue #2167267 by yched, andypost: Remove deprecated field_attach_*_view().

8.0.x
Nathaniel Catchpole 2014-02-12 10:42:13 +00:00
parent f5ba64c9c1
commit 7dcaa39580
22 changed files with 251 additions and 438 deletions

View File

@ -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);
}

View File

@ -89,16 +89,18 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
* {@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 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
}
}
}
// 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 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
$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'];

View File

@ -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

View File

@ -12,9 +12,8 @@ use Drupal\Component\Annotation\Plugin;
/**
* 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().

View File

@ -48,22 +48,20 @@ interface FormatterInterface extends PluginSettingsInterface {
* 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 @@ interface FormatterInterface extends PluginSettingsInterface {
*
* @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);

View File

@ -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 @@ class DateTimeFieldTest extends WebTestBase {
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);

View File

@ -111,8 +111,8 @@ class EmailFieldTest extends WebTestBase {
// 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');
}
}

View File

@ -9,6 +9,7 @@ namespace Drupal\entity\Entity;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\entity\EntityDisplayBase;
/**
@ -197,4 +198,62 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
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;
}
}

View File

@ -19,7 +19,7 @@ use Drupal\field\FieldUpdateForbiddenException;
* 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".
*/

View File

@ -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 @@ use Drupal\entity\Entity\EntityFormDisplay;
* 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.
*

View File

@ -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;
}

View File

@ -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;

View File

@ -142,8 +142,8 @@ class DisplayApiTest extends FieldUnitTestBase {
$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 @@ class DisplayApiTest extends FieldUnitTestBase {
$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)));
}

View File

@ -42,9 +42,9 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
}
/**
* 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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
// 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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
$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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
$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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
'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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
'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 @@ class FieldAttachOtherTest extends FieldUnitTestBase {
}
/**
* 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 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));
// 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.');
// 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.');
}
/**

View File

@ -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);
}
}

View File

@ -516,10 +516,8 @@ class LinkFieldTest extends WebTestBase {
}
$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);
}

View File

@ -61,9 +61,8 @@ use Drupal\Component\Utility\Xss;
* - 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 @@ use Drupal\Component\Utility\Xss;
* - 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)

View File

@ -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.
*

View File

@ -100,11 +100,9 @@ class TermFieldMultipleVocabularyTest extends TaxonomyTestBase {
// 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 @@ class TermFieldMultipleVocabularyTest extends TaxonomyTestBase {
// 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.');

View File

@ -117,11 +117,9 @@ class TermFieldTest extends TaxonomyTestBase {
// 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.

View File

@ -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 @@ class TextPlainUnitTest extends DrupalUnitTestBase {
/**
* 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;
}

View File

@ -139,8 +139,8 @@ class TextFieldTest extends WebTestBase {
// 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 @@ class TextFieldTest extends WebTestBase {
// 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 @@ class TextFieldTest extends WebTestBase {
$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');
}
}