Problem/Motivation
When a user name field has a preferred_field_reference or alternative_field_reference configured, the realname integration resolves those components via AdditionalComponentService::collectRenderedValues(), which renders the referenced field item and catches \Exception around the render call. Outside a render context, Renderer::render() throws LogicException, which is caught and treated as "no value" — the component is silently dropped.
UserHooks::userLoad() runs on every user entity load, and many loads happen with no render context active: drush, cron, queue workers, mail building, and the early phases of ordinary page requests (routing and access checks run before HtmlRenderer wraps the page build in executeInRenderContext()). In those cases $account->realname — and therefore the altered display name — is computed without the preferred/alternative component. Because realname is statically cached per request, the same user can show different names depending on where in the request they were first loaded, and a stale render context left on the renderer's stack can make the bug vanish intermittently — which may be why it is hard to pin down in the wild.
The same silent fallback affects any other caller of the service without an active render context; we first noticed it via a site computing display names in preSave().
A contributing factor: the existing AdditionalComponentServiceTest covers _self, _self_property_* and a missing field, but not a real field reference — so resolveField()/collectRenderedValues(), the only path that renders, currently has no test coverage.
Steps to reproduce
- On the user entity, add a
namefield and a plainstringfield (e.g. "Nickname"). Confirmname.settingsuser_preferrednow holds the name field's machine name (creating the field sets this automatically; note it must be the field name, not a format id). - In the name field's settings, set the preferred source to the nickname field, and set the field's
override_formatto a format whose pattern uses the preferred token with a fallback, e.g.(Gp~Gg)+iGf. - Create a user with name "Lucinda Cooper" and nickname "Cindy".
- Probe without a render context:
drush php:eval '$u = \Drupal\user\Entity\User::load(UID); print $u->getDisplayName();'→ "Lucinda Cooper". - Probe the same load inside
\Drupal::service('renderer')->executeInRenderContext(new \Drupal\Core\Render\RenderContext(), …)(separate drush invocation, to avoid the per-request realname static cache) → "Cindy Cooper".
Proposed resolution
Wrap the viewFieldItem()/render() call in collectRenderedValues() in executeInRenderContext(new RenderContext(), …) — or equivalently use $this->renderer->renderInIsolation($renderable) — so resolution works identically in all calling contexts. No signature or behaviour changes otherwise.
Two adjacent observations we're deliberately leaving out of scope for 1.x, as they'd be behaviour changes in a stable branch: the access('view') check makes computed names viewer-dependent, and the mid-computation render discards cacheability metadata.
Remaining tasks
User interface changes
None.
API changes
None.
Data model changes
None.
Issue fork name-3614450
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
jonathanshawComment #3
bluegeek9 commentedComment #4
bluegeek9 commentedI will look into ways we might be able to fix this. In 2.0.x the preferred and alternative name will be field settings.
#3031696: Refactor preferred and alternative as computed properties
Comment #5
jonathanshawMy suggestion for the 2.x solution for this issue would be #3614726: Add NameItem::getComponents() and NameItem::format() to make name resolution available outside the formatter. In sense this issue is the 1.x bug manifestation of that broader API weakness.