Conversation
…ings-store read target The symfony config write target is read-only once debug mode is off, so the only way to keep Appearance & Branding, perspectives and element tree widgets editable in production is to point both the read and the write target at the settings store. Doing that currently breaks them in two ways: * SettingRepository::loadConfig() destructured the result of loadConfigByKey() only in its caller, so the `!$data` fallback tested a two-element array and never fired. With an empty settings store the admin settings therefore resolved to null and every value configured in the symfony configuration was dropped, leaving an empty Appearance & Branding form. The fallback now works and returns the configured settings, still writeable because the data source stays unset. * listConfigurations() collected its keys with fetchAllKeys(), which merges the settings store ids with the symfony configuration keys, while getConfiguration() honours the read target. A single perspective or widget defined in the symfony configuration therefore made the whole listing fail with a 404. The keys are now taken from the read target when one is configured.
|
Contributor
There was a problem hiding this comment.
Copilot review overview
🟢 Approved
The focused changes correctly resolve both reported configuration-loading failures without altering default behavior.
Review effort: Balanced
Findings: None
What changed in this PR
Fixes settings-store read targets so admin settings retain Symfony defaults and perspective listings remain usable.
Changes:
- Falls back to Symfony admin settings when the settings store is empty.
- Lists only configurations available through the configured read target.
- Preserves existing behavior when no read target is configured.
| File | Description |
|---|---|
src/Setting/Admin/Repository/SettingRepository.php |
Correctly handles repository tuples and admin-setting fallback. |
src/Perspective/Repository/PerspectiveConfigRepository.php |
Aligns listed perspective keys with the read target. |
src/Perspective/Repository/ElementTreeWidgetConfigRepository.php |
Aligns listed widget keys with the read target. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
The
symfony-configwrite target is read-only once debug mode is off (docs), so the documented way to keep Appearance & Branding editable in production is to point both targets at the settings store:Doing exactly that still leaves the screens unusable, in two independent ways.
1. The symfony configuration is discarded as soon as
read_target: settings-storeis setSettingRepository::loadConfig()assigns the tuple returned byLocationAwareConfigRepository::loadConfigByKey()([$data, $dataSource]) to$dataand only destructures it in the caller.!$datais therefore testing a two-element array and is never true, so the settings-store fallback to the container configuration is dead code — and, as written, it would also have returned the wrapper array (['admin_settings' => [...]]) instead of the settings themselves.The settings store only holds an entry once the settings have been saved through the UI at least once. Until then
getConfiguration()resolves tonull, and everything configured underpimcore_studio_backend.admin_settingsis silently dropped.Verified on 2026.x,
APP_ENV=prod, debug off, withGET /settings/adminwrite_targetonly"backgroundShade":"#123456","brandColor":"#ff0000","hideEditImage":true— but"writeable":false(the data source issymfony-config, the write target is not, soisWriteable()refuses)write_target+read_target"backgroundShade":"","brandColor":"","hideEditImage":false,"writeable":true— the whole branding configuration is goneSo today a project can have its branding read from the (git-versioned) YAML or have it editable, never both.
2. A single perspective/widget in the symfony configuration 404s the whole listing
PerspectiveConfigRepository::listConfigurations()andElementTreeWidgetConfigRepository::listConfigurations()collect their keys withfetchAllKeys(), which merges the settings-store ids with the container configuration keys, whilegetConfiguration()loads through the read target and throwsNotFoundExceptionfor anything the read target cannot see.With
studio_perspectives.read_target: settings-storeand one perspective defined in YAML:The perspective editor is then completely unusable — which matches the "perspectives are also not writable" part of the report.
Fix
SettingRepository::loadConfig()destructures the tuple, so the intended fallback to the symfony configuration actually runs, and it returns theadmin_settingsnode rather than its wrapper. The data source deliberately staysnull, so the settings remain writeable and the first save goes to the settings store (which then takes precedence, as before).listConfigurations()implementations take their keys from the read target viafetchAllKeysByReadTargets()when one is configured, and keep usingfetchAllKeys()when none is (in that case both locations are read, so both key sets belong in the listing).Verification
Live on a 2026.x project with
APP_ENV=prod/ debug off:write_target+read_target=settings-store:GET /settings/adminnow returns"backgroundShade":"#123456","brandColor":"#ff0000","hideEditImage":trueand"writeable":true;POST /settings/admin/savereturns 200 and the saved values are read back from the settings store.GET /perspectives/configurationsreturns 200 instead of 404.config_location): unchanged — admin settings still come from the symfony configuration with"writeable":falsein production, and the perspective listing still contains both the settings-store and the YAML perspectives.vendor/bin/codecept run Unit→ OK (594 tests, 1321 assertions),vendor/bin/phpstan analyse→ no errors.No regression test is included: all three repositories construct
LocationAwareConfigRepositoryinternally, which readsSettingsStoreand therefore needs a database, and this bundle only ships a Unit suite. Making this testable would mean introducing a factory seam forLocationAwareConfigRepositoryacross the three repositories, which felt out of scope for a bugfix on a patch branch — happy to add it if you would rather have it.Closes https://github.com/pimcore/service-operations/issues/1225
Related: #1653, pimcore/studio-ui-bundle#3473