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 returnsTRUEonly when\Drupal::routeMatch()->getRouteName()is non-empty.WebformForm::buildFormRequest()deliberately builds the submission form under a route-less request (resolving to aNullRouteMatch) so thatRouteParametersWebformSourceEntitycannot elect thegraphql_serverconfig entity as a spurious source entity. With no route name,hasActiveTheme()isFALSE.
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_twigvalue coverage in the shared fixture andComputedTestacross template variants: static, math, filter, conditional, loop, andmode: 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.
Issue fork graphql_webform-3604010
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 #4
pfrenssen