Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Command/ImportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
91 changes: 91 additions & 0 deletions Model/Processor/ImportProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -103,13 +132,22 @@ public function process()
return;
}

$shouldLock = $this->shouldLockConfig();
$lockData = [];

foreach ($configurationValues as $configPath => $configValue) {
foreach ($configValue as $scopeType => $scopeValue) {
foreach ($scopeValue as $scopeId => $value) {
if ($value === self::DELETE_CONFIG_FLAG) {
$this->configWriter->delete($configPath, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('<comment>[%s] [%s] %s => %s</comment>', $scopeType, $scopeId, $configPath, 'DELETED'));

if ($shouldLock) {
$this->getOutput()->writeln(
sprintf('<info> Note: If this path is locked in app/etc/config.php, remove it manually.</info>')
);
}

continue;
}

Expand All @@ -121,9 +159,62 @@ public function process()

$this->configWriter->save($configPath, $value, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('<comment>[%s] [%s] %s => %s</comment>', $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('<info>Locking values in app/etc/config.php...</info>');

$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(
'<info>%d %s locked in app/etc/config.php.</info>',
count($lockData),
count($lockData) === 1 ? 'value' : 'values'
));
}

/**
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 15 additions & 0 deletions docs/config-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down