Problem/Motivation

On a real (non-CLI) request, WebformElementWebformComputedTwig.computedValue always resolves to an empty string, while the sibling webform_computed_token element on the same built form resolves correctly. Both types share the WebformElementComputedBase.computedValue field resolver, so the divergence is in how the value is computed, not in the GraphQL wiring.

Root cause is an interaction between Webform core and how this module builds the form since #3595276: Expose the webform elements on the built form instead of on the configured form:

  • Webform's WebformComputedTwig::computeValue() bails out and returns '' when !$theme_manager->hasActiveTheme() && PHP_SAPI !== 'cli'. WebformComputedToken::computeValue() has no such guard.
  • WebformThemeManager::hasActiveTheme() is route-based: it returns TRUE only when \Drupal::routeMatch()->getRouteName() is non-empty.
  • WebformForm::buildFormRequest() deliberately builds the submission form under a route-less request (resolving to a NullRouteMatch) so that RouteParametersWebformSourceEntity cannot elect the graphql_server config entity as a spurious source entity. With no route name, hasActiveTheme() is FALSE.

Result: on php-fpm (non-CLI) the Twig guard is hit and the value is empty; the token element, lacking the guard, resolves. This is a regression exposed by the build-form change in #3595276: Expose the webform elements on the built form instead of on the configured form (part of #3595219: 3.0 Exploration): that change made computedValue read from the built form, which is what made the token variant resolve, but the route-less build request leaves the Twig variant gated off on the web path.

The module's test suite does not catch this. Test runners are CLI, so PHP_SAPI !== 'cli' is FALSE and the Twig guard is skipped entirely; the value computes regardless. The existing ComputedTest already asserts a populated Twig computedValue and passes, giving false confidence. Functional (BrowserTestBase) requests run in-process under CLI as well, so they do not reproduce it either. Any regression guard must therefore target the build-time mechanism (a route / active theme being present during the build), not just assert output.

Steps to reproduce

Against a real (non-CLI) endpoint, query a webform that contains a webform_computed_twig element with a static template (e.g. #template: 'Hello, world!', #mode: text):

{ webformById(id: "<id>") { form { elements {
  ... on WebformElementWebformComputedBase { computedValue mode }
} } } }

The Twig element returns computedValue: ""; a webform_computed_token element on the same form returns its populated value.

Proposed resolution

In WebformForm::buildFormRequest(), give the clean build request a parameter-free synthetic route (set _route and a bare _route_object of new Route('/')). \Drupal::routeMatch() then reports a route name during the build, so hasActiveTheme() returns TRUE and Twig computation runs.

Because the synthetic route carries no parameters, RouteParametersWebformSourceEntity::getSourceEntity() still finds no entity, so the load-bearing source-entity invariant of the route-less request is preserved. The theme is left to normal negotiation: the synthetic route is not flagged _admin_route, so AdminNegotiator does not apply and DefaultNegotiator selects the site's default front-end theme. That is the correct theme for a decoupled consumer: the same one that would render the form for an end user on a coupled site. The actual render goes through renderInIsolation(), so no further theme wiring is needed beyond passing the guard.

The alternative, recomputing the Twig value in the producer/resolver instead of reading it from the built form, is rejected: it duplicates core logic, fights the build-form architecture of #3595276: Expose the webform elements on the built form instead of on the configured form, and would still hit the same theme guard unless a route were supplied.

Remaining tasks

  • Add the synthetic route to WebformForm::buildFormRequest() with a comment explaining the theme-guard / source-entity interaction.
  • Add a targeted regression test that the form build exposes a route / active theme. This is the only thing that catches this regression, since the CLI test runner bypasses the Twig guard. Assert the mechanism (a non-empty route name / hasActiveTheme() during the build), not just the output.
  • Expand webform_computed_twig value coverage in the shared fixture and ComputedTest across template variants: static, math, filter, conditional, loop, and mode: html | text | auto. This locks the computed-output shape and guards the build path even though, under CLI, it passes independently of the fix.
  • Run phpunit, phpcs and phpstan before commit.
Command icon 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

pfrenssen created an issue. See original summary.

  • pfrenssen committed dc6d7a93 on 3.x
    fix: #3604010 Computed Twig element returns empty string
    
    By: pfrenssen
    
pfrenssen’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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