Issue #3586001 by mably: persist DomainNegotiationContext across kernel rebuilds

Problem

ModuleInstaller::install() and ::uninstall() rebuild the service container mid-request. The rebuild replaces every stateful service with a fresh instance, including DomainNegotiationContext which is populated by DomainSubscriber::onKernelRequestDomain() at kernel.request time. Nothing re-populates it after the rebuild, so any post-install URL generation (e.g. batch_process()) asking DomainConfigFactoryOverride for per-domain overrides sees an empty context and returns no override.

Fix

Tag domain.negotiation_context with persist so DrupalKernel::persistServices() transfers the same instance from the old container to the new one across the rebuild.

The context is pure state with no dependencies, so it is safe to share across the rebuild. Services that depend on it (DomainNegotiator, DomainConfigFactoryOverride) pick up the persisted instance through normal constructor injection in the rebuilt container. Simpler and cheaper than re-running negotiation, and covers every rebuild trigger, not just the two ModuleInstaller paths.

Additional fix exposed by the persist tag

DomainConfigOverrideEditable::save() had a latent bug: when a form saves a subset of a config's keys, the schema-cast step (updateExistingKeysInNestedArray($moduleOverrides, $data)) copies $data (base + current form changes) back onto $moduleOverrides, overwriting any previously-saved override values for keys not touched by this form. Previously this never fired because the test process's empty context caused addConfigurationsToCurrentDomain() to silently register [null] as the overridable domain, so saves fell through to base storage. With the context preserved, saves actually go through the override path and the bug surfaces.

Fixed by initializing $data in DomainConfigFactoryOverride::getOverrideEditable() with a deep merge of base + existing domain data, so the cast-copy step sees the correct override values.

Tests

Added kernel test DomainContextAfterKernelRebuildTest exercising both install and uninstall paths in one method. Verified that the test fails cleanly on the pre-fix tree (null \!== 'example_com') and passes with the persist tag applied.

DomainConfigUiSavedConfigTest::testSavedConfig had an assertion that relied on $config->get() returning the base value, which only worked when the test process's context happened to be empty. Switched to getOriginal('name', FALSE) so the assertion is robust regardless of whether a domain is active on the test process.

Verified locally: all 76 domain + domain_config kernel tests pass, and the two FunctionalJavascript tests (DomainConfigUiSavedConfigTest and DomainConfigUIMultiFormTest) pass.

Edited by Frank Mably

Merge request reports

Loading