diff --git a/Model/Processor/ImportProcessor.php b/Model/Processor/ImportProcessor.php
index 470b643..56ac276 100644
--- a/Model/Processor/ImportProcessor.php
+++ b/Model/Processor/ImportProcessor.php
@@ -19,6 +19,8 @@
class ImportProcessor extends AbstractProcessor implements ImportProcessorInterface
{
private const DELETE_CONFIG_FLAG = '!!DELETE';
+ private const KEEP_CONFIG_FLAG = '!!KEEP';
+
/**
* @var WriterInterface
*/
@@ -85,46 +87,87 @@ public function __construct(
public function process()
{
$files = $this->finder->find();
- if (0 === count($files) && false === $this->getInput()->getOption('allow-empty-directories')) {
- throw new \InvalidArgumentException('No files found for format: *.' . $this->getFormat());
- } else {
- $this->getOutput()->writeln('No files found for format: *.' . $this->getFormat());
- $this->getOutput()->writeln('Maybe this is expected behaviour, because you passed the --allow-empty-directories option.');
+
+ if (0 === count($files)) {
+ if (false === $this->getInput()->getOption('allow-empty-directories')) {
+ throw new \InvalidArgumentException('No files found for format: *.' . $this->getFormat());
+ } else {
+ $this->getOutput()->writeln('No files found for format: *.' . $this->getFormat());
+ $this->getOutput()->writeln('Maybe this is expected behaviour, because you passed the --allow-empty-directories option.');
+ $this->getOutput()->writeln(' ');
+ }
}
- foreach ($files as $file) {
- $valuesSet = 0;
- $configurations = $this->getConfigurationsFromFile($file);
- foreach ($configurations as $configPath => $configValues) {
- $scopeConfigValues = $this->transformConfigToScopeConfig($configPath, $configValues);
- foreach ($scopeConfigValues as $scopeConfigValue) {
- if ($scopeConfigValue['value'] === self::DELETE_CONFIG_FLAG) {
- $this->configWriter->delete(
- $configPath,
- $scopeConfigValue['scope'],
- $this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope'])
- );
+ $configurationValues = $this->collectConfigurationValues($files);
+ if (0 === count($configurationValues)) {
+ return;
+ }
- $this->getOutput()->writeln(sprintf('%s => %s', $configPath, 'DELETED'));
- $valuesSet++;
+ 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('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, 'DELETED'));
continue;
}
- $this->configWriter->save(
- $configPath,
- $scopeConfigValue['value'],
- $scopeConfigValue['scope'],
- $this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope'])
- );
+ if ($value === self::KEEP_CONFIG_FLAG) {
+ $this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, 'KEPT'));
+
+ continue;
+ }
+
+ $this->configWriter->save($configPath, $value, $scopeType, $scopeId);
+ $this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, $value));
+ }
+ }
+ }
+ }
+
+ /**
+ * @param array $files
+ *
+ * @return array
+ */
+ private function collectConfigurationValues(array $files): array
+ {
+ $buffer = [];
- $this->getOutput()->writeln(sprintf('%s => %s', $configPath, $scopeConfigValue['value']));
+ foreach ($files as $file) {
+ $valuesSet = 0;
+
+ $configurations = $this->getConfigurationsFromFile($file);
+ foreach ($configurations as $configPath => $configValues) {
+ if (!isset($buffer[$configPath])) {
+ $buffer[$configPath] = [];
+ }
+
+ $scopeConfigValues = $this->transformConfigToScopeConfig($configPath, $configValues);
+ foreach ($scopeConfigValues as $scopeConfigValue) {
+ $scopeType = $scopeConfigValue['scope'];
+ $scopeId = $this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope']);
+ $buffer[$configPath][$scopeType][$scopeId] = $scopeConfigValue['value'];
$valuesSet++;
}
}
- $this->getOutput()->writeln(sprintf('Processed: %s with %s value(s).', $file, $valuesSet));
+ if (0 === $valuesSet) {
+ continue;
+ }
+
+ $this->getOutput()->writeln(sprintf(
+ 'Collected configuration values from %s with %s %s.',
+ $file,
+ $valuesSet,
+ $valuesSet === 1 ? 'value' : 'values'
+ ));
}
+
+ $this->getOutput()->writeln(' '); // Add empty line to make output in terminal nicer.
+
+ return $buffer;
}
/**
diff --git a/Test/Unit/Model/Processor/ImportProcessorTest.php b/Test/Unit/Model/Processor/ImportProcessorTest.php
index e1be178..c54362a 100644
--- a/Test/Unit/Model/Processor/ImportProcessorTest.php
+++ b/Test/Unit/Model/Processor/ImportProcessorTest.php
@@ -14,6 +14,7 @@
use Semaio\ConfigImportExport\Model\File\Reader\YamlReader;
use Semaio\ConfigImportExport\Model\Processor\ImportProcessor;
use Semaio\ConfigImportExport\Model\Validator\ScopeValidatorInterface;
+use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ImportProcessorTest extends TestCase
@@ -67,6 +68,9 @@ public function processWithoutFiles(): void
$this->expectException(InvalidArgumentException::class);
$processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
+ $inputMock = $this->getMockBuilder(InputInterface::class)->getMock();
+ $inputMock->method('getOption')->with('allow-empty-directories')->willReturn(false);
+ $processor->setInput($inputMock);
$processor->setFinder($finderMock);
$processor->process();
}
@@ -126,6 +130,16 @@ public function process(): void
0 => '!!DELETE',
],
],
+ 'test/config/custom_field_to_be_keeped' => [
+ 'default' => [
+ 0 => 'VALUE_THAT_SHOULD_NOT_BE_PROCESSED',
+ ],
+ ],
+ 'test/config/custom_field_to_be_keeped' => [
+ 'default' => [
+ 0 => '!!KEEP',
+ ],
+ ],
];
$readerMock = $this->getMockBuilder(YamlReader::class)
@@ -133,7 +147,7 @@ public function process(): void
->getMock();
$readerMock->expects($this->once())->method('parse')->willReturn($parseResult);
- $this->scopeValidatorMock->expects($this->exactly(2))->method('validate')->willReturn(true);
+ $this->scopeValidatorMock->expects($this->exactly(3))->method('validate')->willReturn(true);
$this->configWriterMock->expects($this->once())->method('save');
$this->configWriterMock->expects($this->once())->method('delete');
diff --git a/docs/config-import.md b/docs/config-import.md
index 05bfd53..a54645f 100644
--- a/docs/config-import.md
+++ b/docs/config-import.md
@@ -97,6 +97,18 @@ vendorx/general/api_key:
0: "!!DELETE"
```
+### Keep Config
+
+To ensure that a specific configuration value will not be changed by the config importer, please use the following string as configuration value:
+
+```yaml
+vendorx/general/api_key:
+ default:
+ 0: "!!KEEP"
+```
+
+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.
+
### 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: