diff --git a/Command/ImportCommand.php b/Command/ImportCommand.php
index 4146919..84c80b2 100644
--- a/Command/ImportCommand.php
+++ b/Command/ImportCommand.php
@@ -134,6 +134,13 @@ protected function configure()
'Allow empty directories during import.'
);
+ $this->addOption(
+ 'lock-config',
+ 'c',
+ InputOption::VALUE_NONE,
+ 'Additionally lock imported values in app/etc/config.php (read-only in Admin).'
+ );
+
parent::configure();
}
diff --git a/Model/Processor/ImportProcessor.php b/Model/Processor/ImportProcessor.php
index 56ac276..810eb8b 100644
--- a/Model/Processor/ImportProcessor.php
+++ b/Model/Processor/ImportProcessor.php
@@ -6,7 +6,12 @@
namespace Semaio\ConfigImportExport\Model\Processor;
+use Magento\Config\App\Config\Type\System;
+use Magento\Framework\App\Config\ConfigPathResolver;
use Magento\Framework\App\Config\Storage\WriterInterface;
+use Magento\Framework\App\DeploymentConfig\Writer as DeploymentConfigWriter;
+use Magento\Framework\Config\File\ConfigFilePool;
+use Magento\Framework\Stdlib\ArrayManager;
use Semaio\ConfigImportExport\Exception\UnresolveableValueException;
use Semaio\ConfigImportExport\Model\Converter\ScopeConverterInterface;
use Semaio\ConfigImportExport\Model\File\FinderInterface;
@@ -61,21 +66,45 @@ class ImportProcessor extends AbstractProcessor implements ImportProcessorInterf
*/
private $resolvers;
+ /**
+ * @var DeploymentConfigWriter
+ */
+ private $deploymentConfigWriter;
+
+ /**
+ * @var ConfigPathResolver
+ */
+ private $configPathResolver;
+
+ /**
+ * @var ArrayManager
+ */
+ private $arrayManager;
+
/**
* @param WriterInterface $configWriter
* @param ScopeValidatorInterface $scopeValidator
* @param ScopeConverterInterface $scopeConverter
+ * @param DeploymentConfigWriter $deploymentConfigWriter
+ * @param ConfigPathResolver $configPathResolver
+ * @param ArrayManager $arrayManager
* @param ResolverInterface[] $resolvers
*/
public function __construct(
WriterInterface $configWriter,
ScopeValidatorInterface $scopeValidator,
ScopeConverterInterface $scopeConverter,
+ DeploymentConfigWriter $deploymentConfigWriter,
+ ConfigPathResolver $configPathResolver,
+ ArrayManager $arrayManager,
array $resolvers = []
) {
$this->configWriter = $configWriter;
$this->scopeValidator = $scopeValidator;
$this->scopeConverter = $scopeConverter;
+ $this->deploymentConfigWriter = $deploymentConfigWriter;
+ $this->configPathResolver = $configPathResolver;
+ $this->arrayManager = $arrayManager;
$this->resolvers = $resolvers;
}
@@ -103,6 +132,9 @@ public function process()
return;
}
+ $shouldLock = $this->shouldLockConfig();
+ $lockData = [];
+
foreach ($configurationValues as $configPath => $configValue) {
foreach ($configValue as $scopeType => $scopeValue) {
foreach ($scopeValue as $scopeId => $value) {
@@ -110,6 +142,12 @@ public function process()
$this->configWriter->delete($configPath, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, 'DELETED'));
+ if ($shouldLock) {
+ $this->getOutput()->writeln(
+ sprintf(' Note: If this path is locked in app/etc/config.php, remove it manually.')
+ );
+ }
+
continue;
}
@@ -121,9 +159,62 @@ public function process()
$this->configWriter->save($configPath, $value, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, $value));
+
+ if ($shouldLock) {
+ $lockData[] = [
+ 'path' => $configPath,
+ 'value' => $value,
+ 'scope' => $scopeType,
+ 'scope_id' => $scopeId,
+ ];
+ }
}
}
}
+
+ if ($shouldLock && !empty($lockData)) {
+ $this->writeToConfigPhp($lockData);
+ }
+ }
+
+ /**
+ * Check if --lock-config option is enabled.
+ */
+ private function shouldLockConfig(): bool
+ {
+ $input = $this->getInput();
+ return $input && $input->hasOption('lock-config') && $input->getOption('lock-config');
+ }
+
+ /**
+ * Batch-write collected values to app/etc/config.php.
+ */
+ private function writeToConfigPhp(array $lockData): void
+ {
+ $this->getOutput()->writeln(' ');
+ $this->getOutput()->writeln('Locking values in app/etc/config.php...');
+
+ $configArray = [];
+ foreach ($lockData as $item) {
+ $resolvedPath = $this->configPathResolver->resolve(
+ $item['path'],
+ $item['scope'],
+ $item['scope_id'],
+ System::CONFIG_TYPE
+ );
+ $configArray = $this->arrayManager->set($resolvedPath, $configArray, $item['value']);
+ }
+
+ $this->deploymentConfigWriter->saveConfig(
+ [ConfigFilePool::APP_CONFIG => $configArray],
+ false
+ );
+
+ $this->getOutput()->writeln(sprintf(
+ '%d %s locked in app/etc/config.php.',
+ count($lockData),
+ count($lockData) === 1 ? 'value' : 'values'
+ ));
}
/**
diff --git a/README.md b/README.md
index 6dd2387..2de55b5 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,16 @@ See [docs/file-formats.md](docs/file-formats.md) for more information and exampl
See [docs/config-import.md](docs/config-import.md) for more information.
+#### Lock config values
+
+You can lock imported values in `app/etc/config.php` to make them read-only in the Admin panel:
+
+```bash
+php bin/magento config:data:import config/store production --lock-config
+```
+
+This writes values to both the database and `config.php`. Locked values appear greyed out in Admin > Stores > Configuration. See the [import docs](docs/config-import.md#lock-config) for details.
+
### Export config data
diff --git a/docs/config-import.md b/docs/config-import.md
index 4b4e8c4..b042eb5 100644
--- a/docs/config-import.md
+++ b/docs/config-import.md
@@ -20,6 +20,7 @@ $ php bin/magento config:data:import --help
--recursive (-r) Recursively go over subdirectories and import configs.
--prompt-missing-env-vars (-p) Prompt in interactive mode when environment variables are found but not configured (Default: true)
--allow-empty-directories (-e) Do not throw error if import directories are empty.
+ --lock-config (-c) Additionally lock imported values in app/etc/config.php (read-only in Admin).
```
:exclamation: Only use the `no-cache` option if you clear the cache afterwards, e.g. in a deployment process. Otherwise the changes will have no effect.
@@ -123,6 +124,20 @@ vendorx/general/api_key:
This is helpful when you've got the same settings across different environments but want to keep one environment ( `X` env) unchanged without showing the exact value in the config file. It's a common scenario, especially when dealing with sensitive data. You really should only keep that kind of info in the environment’s database, not in your GIT repo.
+### Lock Config
+
+By default, imported values are written to the database (`core_config_data`) and remain editable in the Admin panel. If you want to additionally lock the values so they become **read-only in Admin**, use the `--lock-config` option:
+
+```bash
+php bin/magento config:data:import config/store production --lock-config
+```
+
+This writes the imported values to both the database **and** `app/etc/config.php`. Values stored in `config.php` take precedence over database values and appear greyed out (non-editable) in Admin > Stores > Configuration.
+
+:exclamation: When using `--lock-config` together with `!!DELETE`, the value is removed from the database but **not** from `config.php`. If the value was previously locked, you need to remove it from `app/etc/config.php` manually.
+
+:exclamation: The `--lock-config` option writes to the filesystem. Make sure `app/etc/config.php` is writable by the PHP process.
+
### Recursive folder setup
If you choose to store your configuration files in subdirectories, e.g. per vendor, the recommended folder setup should look like this: