Updated: Comment #29

Problem/Motivation

Split from #2095195: Remove deprecated field_attach_form_*().

The field_attach_*_view() functions:
- are old D7-style functional code
- live in field.module but are called from Core/Entity, which is backwards
- are still formulated in the old entity translation model (receive an $entity and a $langcode)
- are still largely based on code that predates EntityNG / Entity translation (convoluted field_invoke() / field_invoke_method() iterators)

Proposed resolution

Replace them by simple iterators in EntityViewDisplay objects, that assume that multilingual logic has been resolved upstream:
EntityViewDisplay::build($entity)
EntityViewDisplay::buildMultiple($entities)

Those build the render arrays for all the components set to be visible in the Display, including fields & other 3rd party component types after #1875974: Abstract 'component type' specific code out of EntityDisplay, but without 'extra fields', since "extra fields" have no generic "build callback" to call, only ad-hoc code.

(note: "All components except extra fields" means "just fields" in current core. But after #1875974: Abstract 'component type' specific code out of EntityDisplay, this will really mean "all components except extra fields")

Remaining tasks

None.

User interface changes

None

API changes

Before:

// $entities can span several bundles, $displays is an array keyed by entity bundle
field_attach_prepare_view($entity_type, $entities, $displays, $langcode) 
$build = field_attach_view($entity, $display, $langcode, $options)

After:

// $entities, being displayed by the same $display, necessarily belong to the same bundle here.  
$display->buildMultiple($entities); 

- hook_field_attach_view_alter() is renamed to hook_entity_view_display_build_alter()
its weird & irregular $context['display_options'] param is replaced by $context['display'] (the EntityViewDisplay being used for viewing, which is what the rest of the entity / field view callstack works with too).

See #16 for why this hook is still needed in addition to hook_entity_view_alter().

CommentFileSizeAuthor
#84 field_attach_view_remove-2167267-84.patch51.63 KBandypost
#84 interdiff.txt1.13 KBandypost
#80 interdiff.txt5.69 KByched
#80 field_attach_view_remove-2167267-80.patch51.31 KByched
#77 field_attach_view_remove-2167267-76.patch49.96 KByched
#75 field_attach_view_remove-2167267-75.patch49.96 KByched
#73 interdiff.txt3.25 KByched
#73 field_attach_view_remove-2167267-62.patch49.37 KByched
#62 interdiff.txt3.25 KByched
#62 field_attach_view_remove-2167267-56.patch49.36 KByched
#56 field_attach_view_remove-2167267-56.patch49.36 KByched
#56 field_attach_view_remove-2167267-56.patch49.36 KByched
#51 interdiff.txt708 bytesyched
#51 field_attach_view_remove-2167267-51.patch47.2 KByched
#49 interdiff.txt1.97 KByched
#48 interdiff.txt21.65 KByched
#48 field_attach_view_remove-2167267-46.patch47.25 KByched
#45 interdiff.txt21.65 KByched
#45 field_attach_view_remove-2167267-45.patch46.37 KByched
#40 interdiff.txt3.79 KByched
#40 field_attach_view_remove-2167267-40.patch45.09 KByched
#29 interdiff.txt18.66 KByched
#29 field_attach_view_remove-2167267-29.patch45.03 KByched
#28 interdiff.txt17.86 KByched
#28 field_attach_view_remove-2167267-28.patch43.53 KByched
#27 interdiff.txt4.84 KByched
#27 field_attach_view_remove-2167267-26.patch26.4 KByched
#24 interdiff.txt4.91 KByched
#24 field_attach_view_remove-2167267-24.patch26.67 KByched
#21 interdiff.txt16.6 KByched
#21 field_attach_view_remove-2167267-21.patch25.36 KByched
#6 interdiff.txt5.7 KByched
#6 field_attach_view_remove-2167267-6.patch20.73 KByched
#5 interdiff.txt2.47 KByched
#5 field_attach_view_remove-2167267-5.patch17.57 KByched
#3 interdiff.txt1.54 KByched
#3 field_attach_view_remove-2167267-3.patch14.1 KByched
#1 field_attach_view_remove-2167267-1.patch14.27 KByched
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Status: Active » Needs review
FileSize
14.27 KB

Initial patch.

For now this keeps the existing calls and the functions themselves, just routes them to the new, way simpler methods in EntityViewBuilder.

yched’s picture

Status: Needs work » Needs review
FileSize
14.1 KB
1.54 KB
yched’s picture

patch didn't report the filterEmptyValues() we currently run twice on each field of each displayed entity (which is crazy)

yched’s picture

- Removed the absurd $content['display_options'] argument in hook_field_attach_view_alter(),
replaced it with $content['display'] (= the EntityDisplay), which is what is actually useful for implementors.
- Testing what happens if we remove the equally absurd _field_view_prepared flag.

effulgentsia’s picture

  1. +++ b/core/modules/field/field.api.php
    @@ -366,18 +366,9 @@ function hook_field_attach_extract_form_values(\Drupal\Core\Entity\EntityInterfa
    - * @deprecated as of Drupal 8.0. Use the entity system instead.
    + *   - entity: The entity being rendered.
    + *   - view_mode: The view mode; for example, 'full' or 'teaser'.
    + *   - display: The EntityDisplay holding the display options.
      */
     function hook_field_attach_view_alter(&$output, $context) {
    

    Do we still want this hook? If we do, seems like we should at least rename it. Maybe something like hook_field_view_alter()? Related to that, should we invoke it on one field at a time instead of on the whole collection? There's already hook_entity_view_alter() for messing with the collection.

  2. +++ b/core/modules/field/field.deprecated.inc
     function field_attach_prepare_view($entity_type, array $entities, array $displays, $langcode = NULL) {
    ...
     function field_attach_view(EntityInterface $entity, EntityViewDisplayInterface $display, $langcode = NULL, array $options = array()) {
    

    If we remove these functions entirely, then can we make the new methods in EntityViewBuilder protected instead of public?

catch’s picture

Do we still want this hook? If we do, seems like we should at least rename it. Maybe something like hook_field_view_alter()? Related to that, should we invoke it on one field at a time instead of on the whole collection? There's already hook_entity_view_alter() for messing with the collection.

These were always duplicates, or in most cases pre-dated the generic entity hooks that were added later. I think we could remove them?

Wim Leers’s picture

Anecdotal info: Edit module uses hook_field_attach_view_alter() in Drupal 7, to easily override the output of a single field in case of a specific display mode.

effulgentsia’s picture

Exactly. The only real usefulness of hook_field_attach_view_alter() is to alter the output of 1 or more fields that are rendered separately from entity_view(). The only use case that we have of that in core is field_view_field() which is called by edit.module and the "field" Views plugin. That's why I suggest renaming it to hook_field_view_alter() and making it operate on one field at a time. However, if we have such a hook, then we must also call it during the entity_view() pipeline as well, which means in that pipeline, replacing a single call to hook_field_attach_view_alter() with as many calls to hook_field_view_alter() as there are fields being rendered. I think that would be a conceptual improvement (the difference between hook_field_view_alter() and hook_entity_view_alter() would become much clearer than the current difference between hook_field_attach_view_alter() and hook_entity_view_alter()). However, it potentially brings up performance concerns of invoking more function calls to get the same job done. But, I suspect the perf difference won't be very big, and now that we have entity render caching, a small perf cost would matter less, as it will get cached anyway.

effulgentsia’s picture

Alternatively, per #2095195-42: Remove deprecated field_attach_form_*(), we might move the field_attach concept to EntityDisplay, which means we can invoke a single hook, like hook_entity_display_view_alter() (or better name TBD) on the entire output of what that EntityDisplay is responsible for (whether that's one field or many). That would essentially be a direct replacement for the current hook_field_attach_view_alter(), but would make more conceptual sense, because an EntityDisplay is an actual thing, whereas "field_attach" begs the question "attached to what?". But, for this to make sense, we would need to make field_view_field() either take an EntityDisplay parameter, or create a mock one. Which I think boils down to the question: how do we want people conceptualizing the process of rendering a single field in isolation? as something that involves an EntityDisplay as the definitive domain object controlling all display settings, or not? If yes, then I think hook_entity_display_view_alter() and no hook_field_view_alter() would be good. Otherwise, I think hook_field_view_alter() and no hook_entity_display_view_alter() would be better.

sun’s picture

Issue tags: +@deprecated
Wim Leers’s picture

+1 for #12/#13.

yched’s picture

Sorry for staying silent, busy week :-/ A couple clarifications:

The intent of field_attach_view_alter() is to let contrib "alter the output of formatters, whenever they get rendered" - Dave Reid has a lot of modules that rely on it :-)

It does not duplicate hook_entity_view_alter(), since that one doesn't run on single-field renders, aka field_view_field() / field_view_value().

field_view_field() is currently used by:
- views for "field" Views (as opposed to "Views that display fully rendered entities"), e.g. table views; (#1867518: Leverage entityDisplay to provide fast rendering for fields has a plan to drastically optimize that pipeline, conceptually simple but not fully trivial to code :-/)
- edit.module
- ... contrib & custom cases (e.g custom preprocessors)

field_view_value() is "format a single $item with a formatter". No real uses cases in core, but is a handy tool for custom code (in CCK D5, content_format() was our only answer to "please don't print unsafe raw values, use formatters").

On the widgets side there's hook_field_widget_alter() that runs for each widget and thus covers all cases, but back in D7 we explicitly avoided doing the same on the "view" side, for performance concerns (potentially 100s invocations on a listing page). AFAIK the render cache cannot really make that more sustainable, since it only kicks in after the render array is built ?

So yes, as much as I'd like to say "deprecated in favor of the entity hooks", there's a still a need for a hook to alter formatters on both "all fields on full entity view", and on single-field view. The name of that hook can obviously be adjusted, I just didn't go there yet :-)

yched’s picture

Then, basically, agreed with #13. The current plan is to move field_attach_view() to the EntityDisplay, and then the hook should probably follow.

Note that the current patch moves in that direction already, since it changes field_view_field() to "render fields in an EntityDisplay that only contains one visible field".
I.e, re:

we would need to make field_view_field() either take an EntityDisplay parameter, or create a mock one

Agreed, and the current patch does the latter. field_view_field() IMO still needs to support its two "modes" (either takes a view_mode name, or an array of arbitrary display settings - both internally get resolved to "an EntityDisplay"), and saving callers the task of building the EntityDisplay themselves in the second case is more DX friendly IMO.

yched’s picture

The direction we settle for here impacts what we want to do with
- entity_get_render_display() : #2090509: Optimize entity_get_render_display() for the case of "multiple entity view"
- field_view_field() / field_view_value() : #2134887: move field_view_field() / field_view_value() to methods (the plan over there was to add view() methods on FieldItemList / FieldItem, as shortcuts to implementations living in the EntityViewBuilder, since this was also the intended location for field_attach_*_view() back then).

So I'd like to put the big picture in one place, just to check we're in agreement :

- entity_get_display($entity_type, $bundle, $view_mode)
--> static EntityDisplay::getDisplay($entity_type, $bundle, $view_mode)
internally: as currently (reads from config, or creates a fresh runtime object if not found)

- entity_get_render_display($entity, $view_mode)
--> static EntityDisplay::getRenderDisplays($entities, $view_mode)
Internally: optimized for multiple load, + as currently, invokes hook_entity_display_alter() on each $display

- field_attach_view($entity, $display)
--> $display->attachFields($entity) (name TBD)
This takes care of invoking hook_field_attach_view_alter() (hook name TBD)

Meaning, EntityViewBuilder->viewMultiple()
calls EntityDisplay::getRenderDisplays($entities, $view_mode),
then $display->attachFields($entity)

- field_view_field($entity, $field_name, $view_mode)
--> $entity->{$field_name}->view($view_mode)
== static EntityDisplay::viewField($items, $view_mode)
Internally: calls ::getRenderDisplay(), sets all but $items->getFieldName() to 'hidden', calls $display->attachFields($items->getEntity), returns the right sub render array.

- field_view_field($entity, $field_name, $options)
--> $entity->{$field_name}->view($options)
== static EntityDisplay::viewField($items, $options)
Internally: creates a fresh/empty runtime Display, sets $options for $items->getFieldName(), then same as above

- field_view_value($entity, $field_name, $item, $view_mode)
--> $entity->{$field_name}[$delta]->view($view_mode)
== static EntityDisplay::viewFieldItem($item, $view_mode)
Internally: builds an $items with $item as the only element (like field_view_value() currently), calls viewField($items, $view_mode), returns the right sub-render array

- field_view_value($entity, $field_name, $item, $options)
--> $entity->{$field_name}[$delta]->view($options)
== static EntityDisplay::viewFieldItem($item, $options)
Internally: builds $items as above, calls viewField($items, $options)

Which IMO doesn't look too bad ?

effulgentsia’s picture

AFAIK the render cache cannot really make that more sustainable, since it only kicks in after the render array is built ?

That's true for HEAD, but #2099131: Use #pre_render pattern for entity render caching is a critical beta blocker to change that.

On the widgets side there's hook_field_widget_alter() that runs for each widget

For me, that's another DX argument in favor of making the formatter one a one-at-a-time hook. However, in light of #18, I don't think we need to settle that now, since in either case, we'll be invoking the hook from EntityDisplay, so I think we can proceed rolling patches here with the hook as a multiple field one, and perhaps switch it to a single field one after #2099131: Use #pre_render pattern for entity render caching lands or someone wants to otherwise make a case for the perf cost being trivial, and the DX gain being nontrivial. I'm not quite ready to make the first part of that case yet.

The proposal in #18 looks good to me, except I'm not crazy about the view*() static methods in EntityDisplay. It seems like they wouldn't have much logic in them; why not have that logic inlined into the (non-static) view() of FieldItemList/FieldItem instead?

yched’s picture

Wasn't aware of #2099131: Use #pre_render pattern for entity render caching (well, it seems I once clicked its "follow" button).
Agreed to revisit later.

the view*() static methods in EntityDisplay. It seems like they wouldn't have much logic in them; why not have that logic inlined into the (non-static) view() of FieldItemList/FieldItem instead?

In #2134887-2: move field_view_field() / field_view_value() to methods, @fago was reluctant to have "display business logic" in the "value" domain objects, and I can agree with that - same as $field->access() is a mere wrapper around a call to a method in an AccessController.

We're probably still talking about 10-ish lines of wrapping logic around a call to $display->attachFields(). I guess we'll need to actually write them and see how this looks.

Another approach would be to keep them in EntityViewBuilder:
$item->view($view_mode) == $view_builder->viewFieldItem($item, $view_mode)
calls EntityDisplay::getRenderDisplay(), prepares $display, calls $display->attachFields(), extracts the result.

yched’s picture

updated patch for the current approach :
- field_attach_prepare_view() --> EntityDisplay::prepareFields()
- field_attach_view() --> EntityDisplay::attachFields() (invokes hook_field_attach_view())
- field_view_field() calls EntityDisplay::attachFields() on a $display with one single field.

Interdiff attached, but the patch probably makes more sense on its own.

@todo :
- agree on the method names
- rename hook_field_attach_view_alter() to ? hook_field_view_alter() ?

yched’s picture

- Fixes fails:
rdf currently has mappings for things that are not displayed with formatters (e.g term.description - for no purpose, nothing prints the resulting attributes right now), so initializing the $items->_attributes element cannot be done just for formatters.
Patch puts this step back in EntityViewBuilder::buildContent() as in current HEAD

- code simplification: EntityDisplay can use $this->getFieldDefinitions() to iterate on "fields that are displayed with formatters"

yched’s picture

Er, scratch patch #24, wrong copy paste, too much looping.

Updated patch & interdiff against #21.

yched’s picture

Removes the functions, and move the existing calls over to the new API.

yched’s picture

Gave some more thought on naming:

- $display->prepareFields($entities) works IMO

- $display->attachFields($entity) doesn't. We're not "attaching fields" to either the display nor the entity.
"attach" is a remnant of D7's "field attach API", which was "the API that entity types needed to call if they wanted to have fields attached to their entities". It's an obsolete formulation in D8.

--> renamed to $display->viewFields($entity).
This is in line with EntityViewBuilder::view($entity) / viewMultiple($entities), which established "view <-> return a render array".
Here, we're doing this for the fields handled by the Display.

--> accordingly, renamed hook_field_attach_view_alter() to hook_fields_view_alter()
This is in line with hook_entity_view_alter().

yched’s picture

Issue summary: View changes
yched’s picture

Issue summary: View changes
yched’s picture

Issue summary: View changes
yched’s picture

Green. This should be ready for reviews now.

yched’s picture

One more remark:
For extra simplicity, we might consider merging prepareFields($entities) & viewFields($entity) into one single viewFields($entities) method, that takes care of calling both $formatter->prepareView() & ->view().

The only thing that's stopping us is that hook_entity_prepare_view() currently runs between the calls to prepareFields() & viewFields(). Not sure if that's worth keeping.

Thoughts ?

sun’s picture

+++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php
@@ -71,4 +72,48 @@
+    \Drupal::moduleHandler()->alter('fields_view', $build, $context);

Hm. "fields" is a bit out of scope of everything...?

Can we make that hook_entity_display_view_alter()?

yched’s picture

@sun we already have hook_entity_display_alter(), which #2165835: Rename EntityDisplay to EntityViewDisplay in accordance with its interface is renaming to hook_entity_view_display_alter(). That hook lets you alter the "EntityViewDisplay" object before starting to generate render arrays based on the settings it contains.

So, -1 for hook_entity_display_view_alter() here :-)

The hook we're talking about here is about altering the "view" (the render array) for the fields in an entity. Thus hook_fields_view_alter() seems consistent to me.

Other possibility: as discussed in #16, this hook is an equivalent to hook_field_widget_form_alter() on the formatters side - but is called once for all fields in the entity rather than field per field for performance reasons.

So maybe hook_field_formatters_view_alter() then ?

catch’s picture

Quite like hook_field_formatters_view_alter() tbh.

yched’s picture

Works for me too. Renamed the hook (+ patch reroll)

effulgentsia’s picture

Quite like hook_field_formatters_view_alter() tbh.

I like that hook name for the code in its current state, but will it continue to make sense after #1875974: Abstract 'component type' specific code out of EntityDisplay? Would we be allowed to change it again in that issue, or do we get only one chance to name it for D8, and therefore need to pick a name that will still make sense after that abstraction?

yched’s picture

The scope of #1875974: Abstract 'component type' specific code out of EntityDisplay is just to be able to internally recognize different "kinds" of components in the EntityDisplay, not affecting how the rendering of those components happens.
Whatever happens there, we'll still need a function that just renders field formatters, and runs a hook on that, so the hook name should still apply IMO.

effulgentsia’s picture

Whatever happens there, we'll still need a function that just renders field formatters

Apologies, perhaps I'm not enough up to speed on that issue, but I'm surprised by this. Suppose an example of a non-field component is EVA. If I understand correctly, that would be rendered by something that's not a field formatter. So:

+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -106,16 +108,21 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
+      $entity->content += $displays[$entity->bundle()]->viewFields($entity);

Wouldn't this then need to become $entity->content += $displays[$entity->bundle()]->viewComponents($entity);? And the alter hook similarly renamed? What would be the purpose of a function that renders only the field components separately from non-field components, especially since:

1) you might want to sandwich your EVA in between two fields, in terms of display weight? and
2) nothing about the signature of hook_field_formatters_view_alter() is specific to fields or formatters

Note: I'm not trying to argue for expanding the scope of either issue, just trying to understand what the vision of the end state is once the two are integrated.

yched’s picture

What would be the purpose of a function that renders only the field components separately from non-field components

Drats - I thought the prepareFields() / attachFields() part of #18 had been agreed upon :-/.

EVA are currently "extra fields". Extra fields are special cases - by definition, there's no generic rendering step for "extra fields", those are "arbitrary stuff that take care of adding themselves at some point in the build process".
The Display cannot be in charge of the "build extra fields" step, since "extra fields" have no generic "build callback" to call (If they did, what I argue in #1954188-14: Revaluate the concept of 'extra fields' is that they could probably be equated to Blocks, and while that's totally an option for D9, it's way too late for D8 IMO)
All the Display can do for extra fields is hold configured settings (visible / hidden + weight) and hand them back to whatever code does the building.

Then there's the case of "other component types", which might have "generic rendering callbacks" - like field_groups, or like EVA if they decided to stop being "extra fields" and upgrade themselves to be their own "display component type".

The scope of #1875974: Abstract 'component type' specific code out of EntityDisplay is currently only about making Displays know how to store display settings for 3rd party 'component types', not about building the render arrays too.

We can bite the bullet and say we'll include a unified API for "build the render array for all components set to be displayed, whatever their type" - but that will necessarily leave "extra fields" out.

Would mean :
- EntityDisplay->viewMultiple($entities)
This delegates to a viewMultiple() method for each 'component type' handler.
For fields, this does the equivalent of the current prepareFields() (aka f_a_prepare_view()) + viewFields() (aka f_a_view())
This also triggers a hook_[???]_view_alter() on each entity.
- EntityDisplay->view($entity)
This is a shortcut to ->viewMultiple(array($entity))

At this point, what remains in EntityViewBuilder is mostly:
- resolution of the $view_mode into the relevant EntityDisplays,
- addition of the ad-hoc render arrays for the "extra fields" owned by the entity type provider itself.

yched’s picture

Issue summary: View changes
FileSize
46.37 KB
21.65 KB

OK, 3rd approach then :-)

As per #44:
EntityDisplay::view($entity)
EntityDisplay::viewMultiple($entities)
Those build the render arrays for all the components set to be visible in the Display, including fields & other 3rd party component types after #1875974: Abstract 'component type' specific code out of EntityDisplay, but without 'extra fields'.

TODO : find a name for the alter hook triggered in there, proposals welcome.
hook_entity_view_display_alter() is already taken, to alter the options held in the Display before rendering happens.

yched’s picture

Status: Needs work » Needs review
FileSize
47.25 KB
21.65 KB

If we want this to be self contained, Display::view() should assign weights.
(canceled test for the previous patch)

yched’s picture

FileSize
1.97 KB

Wrong interdiff for #48, here's the correct one.

yched’s picture

Status: Needs work » Needs review
FileSize
47.2 KB
708 bytes

no drupalSetContent() in DUTB...

andypost’s picture

I'd like to mention that this field-related code should move to FieldComponentHandler plugin in #1875974: Abstract 'component type' specific code out of EntityDisplay so this hook actually alters the build array for fields only excluding other components.

Also I suppose that this methods builds render so maybe better to use build() and buildMultiple() names?

  1. +++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
    @@ -7,9 +7,44 @@
    +   * Returns a renderable array for the components of an entity.
    ...
    +   * Returns a renderable array for the components of the provided entities.
    

    s/the/a
    components? this works on fields only excluding other components

  2. +++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
    @@ -7,9 +7,44 @@
    +   * This only includes the components handled by the Display object, but
    +   * excludes 'extra fields', that are typically rendered through specific,
    ...
    +   * 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.
    ...
    +   * ad-hoc code in EntityViewBuilderInterface::buildContent() or in
    +   * hook_entity_view() implementations.
    

    Maybe better use fields, once other "components" are attached via ad-hoc code?

  3. +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php
    @@ -71,4 +72,63 @@ public function getRenderer($field_name) {
    +      \Drupal::moduleHandler()->alter('field_formatters_view', $build[$key], $context);
    ...
    +      // @todo Re-rename the hook...
    
    +++ b/core/modules/field/field.api.php
    @@ -358,28 +358,17 @@ function hook_field_attach_extract_form_values(\Drupal\Core\Entity\EntityInterfa
    + *   - entity: The entity being rendered.
    + *   - view_mode: The view mode; for example, 'full' or 'teaser'.
    + *   - display: The EntityDisplay holding the display options.
    ...
    +function hook_field_formatters_view_alter(&$output, $context) {
    ...
    + * Perform alterations on fields being rendered in an entity.
    

    Maybe hook_entity_display_build_alter() better describes?

yched’s picture

@andypost:

Display::build() / hook_entity_display_build_alter() : why not.

Note that after #2165835: Rename EntityDisplay to EntityViewDisplay in accordance with its interface it would become hook_entity_*view*_display_build_alter().

We'd have:
- hook_entity_view_display_alter() : alter the Display before it generates the render arrays
- hook_entity_view_display_build_alter() : alter the generated render arrays.

And similarly on the form side:
- hook_entity_form_display_alter()
- hook_entity_form_display_build_alter()

Does that sound OK ?

1. 2. : see #43 / #44
The current approach is that Display::view() (or build() or whatever) lets the display generate render arrays for all components set to be visible - except "extra fields", since "extra fields" have no generic render logic, only ad-hoc code.

"All components except extra fields" means "just fields" in current core. But after #1875974: Abstract 'component type' specific code out of EntityDisplay, this would really mean "all components except extra fields".

So yes, EntityDisplay:build() would:
- delegate to a build() method in each component handler (the code that runs formatters in the current patch would move to the "field component handler"),
- and then call the _build_alter hook, the scope of which is the render arrays of all components.

andypost’s picture

@yched Thanx a lot! Suppose this should be added to summary!

amateescu’s picture

Display::build() / hook_entity_display_build_alter() : why not.

This means the issue is actually NW, right?

yched’s picture

- Renamed Display::view() / viewMultiple() to buidl() / buildMultiple()

- The "alter the render array" hook is now hook_entity_display_build_alter()
Moves from field.api.php to entity.api.php

(/me secretely hopes we're done with names :-p)

yched’s picture

Issue summary: View changes

Updated the issue summary accordingly.

amateescu’s picture

Apart from a few small documentation issues, the patch looks ready to me as well but I guess we could use a final confirmation from @effulgentsia.

  1. +++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
    @@ -7,9 +7,48 @@
    +   * See the collectRenderDisplays() method for details.
    

    This doesn't like it belongs to this patch :)

  2. +++ b/core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php
    @@ -12,9 +12,8 @@
    + * instanciated and invoked by an EntityDisplay object.
    

    Typo: instantiated

  3. +++ b/core/modules/system/entity.api.php
    @@ -584,6 +584,35 @@ function hook_entity_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplay
    + * @param $build
    ...
    + * @param $context
    

    Missing 'array' type hints.

yched’s picture

Issue summary: View changes
yched’s picture

1. LOL - patch mashup
2. I think I'll never learn to write "instantiate" correctly
3. Yup, the hints were missing from the old hook

+ fix tests (forgot a couple method renames)

yched’s picture

Status: Needs work » Needs review
webchick’s picture

"The test did not complete due to a fatal error."

This has been happening depressingly often as of late. OOM, most likely.

yched’s picture

Status: Needs work » Needs review
FileSize
49.37 KB
3.25 KB

Oh gee. I uploaded the wrong patch in #62...
/me slaps himself in the face with a phonebook, then goes hide in the closet.

Interdiff #62 was correct, but reuploading it here.

Any chance a good soul with admin rights could delete comments #63 -> #72 ? - this issue is hard enough to get consensus on without 10 comments of silly retests...

Status: Needs review » Needs work

The last submitted patch, 73: field_attach_view_remove-2167267-62.patch, failed testing.

yched’s picture

Status: Needs review » Needs work

The last submitted patch, 75: field_attach_view_remove-2167267-75.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
49.96 KB

Forgot one 'entity_display' / 'entity_view_display' rename

Berdir’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
    @@ -15,9 +15,9 @@
       /**
        * Build the structured $content property on the entity.
        *
    -   * @param array $entities
    +   * @param \Drupal\Core\Entity\EntityInterface[] $entities
        *   The entities, implementing EntityInterface, whose content is being built.
    

    Yay for better types. The interface doesn't need to be repeated anymore in the description, then. And I'd suggest to use ContentEntityInterface everywhere you do this, because only those can have fields. I've started doing this piece by piece in the storage controller methods for example.

  2. +++ 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');
    

    I assume we will rename the test class in attach_form() ?

Other than that, this looks great and I think ready, 1. above could also be done on commit, as it's just a documentation change and no re-roll will be necessary anymore. Based on comments above, effulgentsia needs to have a final look at this? Should we assign it to him then?

effulgentsia’s picture

This looks good to me. Thanks, yched, for all the iterations. Once #78 is addressed, I think this can be RTBC.

yched’s picture

re @Berdir #78:

1. Right - switched to ContentEntityInterface in most places, *except* in the phpdoc of EntityViewBuilderInterface::buildContent()...
It's a bit of a WTF IMO, but EntityViewBuilderInterface is currently used by a non-ContentEntity : BlockViewBuilder (extends EntityViewBuilder).
Its viewMultiple() implementation doesn't call buildContent(), and its buildContent() implementation is empty, but still... Switching to ContentEntityInterface there wouldn't be accurate.
(actually, I don't understand why buildContent() is part of EntityViewBuilderInterface - the public entry points should be view() / viewMultiple() ?)

What is true though, is that EntityDisplays are only used for ContentEntities, thus adjusted the hints in this area.
(+ field_view_field() / field_view_value() were inconsistent, one hinted on EntityInterface, the other on ContentEntityInterface)

2. Yes, ultimately those tests should move out of field.module and to a test class in Core/Field
I'd rather do that in a followup, to limit patch size (also, moving tests around can be done at any time before or even after release)

Status: Needs review » Needs work

The last submitted patch, 80: field_attach_view_remove-2167267-80.patch, failed testing.

Berdir’s picture

The last submitted patch, 80: field_attach_view_remove-2167267-80.patch, failed testing.

andypost’s picture

Fixed datetime test - entity_get_render_display() is gone in #2090509: Optimize entity_get_render_display() for the case of "multiple entity view"
The second failed test looks like bot flux, I can't reproduce it locally... seems $images = $this->drupalGetTestFiles('image'); failed

Berdir’s picture

This looks great, do we need a new change notice? If so, we should prepare a draft. If we will simply update an existing one, this is RTBC I think.

yched’s picture

No, this will probably require a change notice of its own (+ updates of a couple ones).
Will work on a draft, but probably won't be before a couple days.

effulgentsia’s picture

Status: Needs review » Reviewed & tested by the community

It's not Feb. 14th yet, so this can be RTBC ahead of the draft change notice.

Berdir’s picture

Works for me, let's not block a critical on that. I have seen issues where that was already enforced, so I wanted to avoid to get stuck due to that :)

andypost’s picture

Filed draft https://drupal.org/node/2193813 that needs better wording and examples

catch’s picture

Status: Reviewed & tested by the community » Fixed

Couldn't find anything to complain about. Committed/pushed to 8.x, thanks!

Berdir’s picture

Title: Remove deprecated field_attach_*_view() » Change record: Remove deprecated field_attach_*_view()
Priority: Critical » Major
Status: Fixed » Active
Issue tags: +Missing change record, +Needs change record

Yay. Let's keep this open for a moment to complete the change notice and update existing ones.

yched’s picture

Awesome! Will finish change notices in the next couple days.

Then, see you in
- #2095195: Remove deprecated field_attach_form_*()
- #2134887: move field_view_field() / field_view_value() to methods

yched’s picture

Issue summary: View changes
yched’s picture

Title: Change record: Remove deprecated field_attach_*_view() » Remove deprecated field_attach_*_view()
Status: Active » Fixed
Issue tags: -Missing change record, -Needs change record

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.