Our bug was views-view-field.html.twig
coming back empty for the entity API field track_icon
on the node 80136.
The template only prints the output
variable.
Where is that coming from?
function template_preprocess_views_view_field(&$variables) {
$variables['output'] = $variables['field']->advancedRender($variables['row']);
}
In FieldPluginBase::advancedRender
I set a breakpoint
$values->_entity->id() == 80136 && $this->table == 'taxonomy_term__track_icon'
we hit my breakpoint exactly twice as expected because we print this node twice
the second time $raw_items = $this->getItems($values);
comes back empty (not good!)
EntityField::getItems()
runs $build_list = $this->getEntityFieldRenderer()->render($values, $this);
in turn EntityFieldRenderer::render
runs into Pick the render array for the row / field we are being asked to render, and remove it from $this->build to free memory as we progress.
the meat is $build = $this->build[$row->index][$field_id];
now, $build
when broken only contains cache metadata, nothing else. Just a #cache
key, with context and tags.
Paging through the class, it’s not a lot of code, we find the buildFields
method which iterates every row in the result and calls EntityViewDisplay
to run formatters:
$display_build = $display->buildMultiple($bundle_entities);
EntityViewDisplay::buildMultiple
has this gem
$build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items, $view_langcode) : [];
// Apply the field access cacheability metadata to the render array.
$this->renderer->addCacheableDependency($build_list[$id][$name], $field_access);
that is beyond suspicious
because that’s where the “nothing but cacheable metadata” might very well come from
so we slap a $name === 'track_icon' && !$build_list[$id][$name]
breakpoint here (we know $name
is the field name because foreach ($this->getComponents() as $name => $options) {
doesnt fire
darn
that was a good shot
let’s try $name === 'track_icon' && !isset($build_list[$id][$name]['#theme'])
that fires the expected amount of times, yay
so i set a breakpoint on
$build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items, $view_langcode) : [];
itself for name track_icon and id 1 and step through
bingo
we ran afoul of
if (static::$recursiveRenderDepth[$recursive_render_id] > static::RECURSIVE_RENDER_LIMIT) {
in EntityReferenceEntityFormatter::viewElements
The recursion protection is only increased ever, never decreased.
Despite the name of the constant RECURSIVE_RENDER_LIMIT
the doxygen accurately tells you it actually has nothing to do with recursion and this is a feature not a bug:
The number of times this formatter allows rendering the same entity.
I will, for now, live without this feature.