diff --git a/Model/Processor/ImportProcessor.php b/Model/Processor/ImportProcessor.php
index 56ac276..c7730f6 100644
--- a/Model/Processor/ImportProcessor.php
+++ b/Model/Processor/ImportProcessor.php
@@ -119,6 +119,12 @@ public function process()
continue;
}
+ if ($this->shouldSkipConfig($configPath, $scopeType, $scopeId, $value)) {
+ $this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, 'SKIPPED'));
+
+ continue;
+ }
+
$this->configWriter->save($configPath, $value, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('[%s] [%s] %s => %s', $scopeType, $scopeId, $configPath, $value));
}
@@ -126,6 +132,24 @@ public function process()
}
}
+ /**
+ * @param string $configPath
+ * @param string $scopeType
+ * @param int $scopeId
+ * @param mixed $value
+ *
+ * @return bool
+ */
+ private function shouldSkipConfig($configPath, $scopeType, $scopeId, $value)
+ {
+ if (is_array($value) && (isset($value['if-not-set']) || (isset($value['if']) && $value['if'] === 'not-set'))) {
+ $existingValue = $this->configWriter->get($configPath, $scopeType, $scopeId);
+ return $existingValue !== null;
+ }
+
+ return false;
+ }
+
/**
* @param array $files
*
diff --git a/Test/Unit/Model/Processor/ImportProcessorTest.php b/Test/Unit/Model/Processor/ImportProcessorTest.php
index c54362a..d9bdd9a 100644
--- a/Test/Unit/Model/Processor/ImportProcessorTest.php
+++ b/Test/Unit/Model/Processor/ImportProcessorTest.php
@@ -157,4 +157,152 @@ public function process(): void
$processor->setReader($readerMock);
$processor->process();
}
+
+ /**
+ * @test
+ */
+ public function processWithIfNotSetModifier(): void
+ {
+ $finderMock = $this->getMockBuilder(Finder::class)
+ ->onlyMethods(['find'])
+ ->getMock();
+ $finderMock->expects($this->once())->method('find')->willReturn(['abc.yaml']);
+
+ $parseResult = [
+ 'test/config/custom_field_if_not_set' => [
+ 'default' => [
+ 0 => [
+ 'if-not-set' => true,
+ 'default' => 1,
+ ],
+ ],
+ ],
+ ];
+
+ $readerMock = $this->getMockBuilder(YamlReader::class)
+ ->onlyMethods(['parse'])
+ ->getMock();
+ $readerMock->expects($this->once())->method('parse')->willReturn($parseResult);
+
+ $this->scopeValidatorMock->expects($this->once())->method('validate')->willReturn(true);
+ $this->configWriterMock->expects($this->once())->method('save');
+ $this->configWriterMock->expects($this->once())->method('get')->willReturn(null);
+
+ $processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
+ $processor->setOutput($this->outputMock);
+ $processor->setFinder($finderMock);
+ $processor->setReader($readerMock);
+ $processor->process();
+ }
+
+ /**
+ * @test
+ */
+ public function processWithIfNotSetModifierAndExistingValue(): void
+ {
+ $finderMock = $this->getMockBuilder(Finder::class)
+ ->onlyMethods(['find'])
+ ->getMock();
+ $finderMock->expects($this->once())->method('find')->willReturn(['abc.yaml']);
+
+ $parseResult = [
+ 'test/config/custom_field_if_not_set' => [
+ 'default' => [
+ 0 => [
+ 'if-not-set' => true,
+ 'default' => 1,
+ ],
+ ],
+ ],
+ ];
+
+ $readerMock = $this->getMockBuilder(YamlReader::class)
+ ->onlyMethods(['parse'])
+ ->getMock();
+ $readerMock->expects($this->once())->method('parse')->willReturn($parseResult);
+
+ $this->scopeValidatorMock->expects($this->once())->method('validate')->willReturn(true);
+ $this->configWriterMock->expects($this->never())->method('save');
+ $this->configWriterMock->expects($this->once())->method('get')->willReturn('existing_value');
+
+ $processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
+ $processor->setOutput($this->outputMock);
+ $processor->setFinder($finderMock);
+ $processor->setReader($readerMock);
+ $processor->process();
+ }
+
+ /**
+ * @test
+ */
+ public function processWithIfNotSetModifierAlternativeSyntax(): void
+ {
+ $finderMock = $this->getMockBuilder(Finder::class)
+ ->onlyMethods(['find'])
+ ->getMock();
+ $finderMock->expects($this->once())->method('find')->willReturn(['abc.yaml']);
+
+ $parseResult = [
+ 'test/config/custom_field_if_not_set' => [
+ 'default' => [
+ 0 => [
+ 'if' => 'not-set',
+ 'default' => 1,
+ ],
+ ],
+ ],
+ ];
+
+ $readerMock = $this->getMockBuilder(YamlReader::class)
+ ->onlyMethods(['parse'])
+ ->getMock();
+ $readerMock->expects($this->once())->method('parse')->willReturn($parseResult);
+
+ $this->scopeValidatorMock->expects($this->once())->method('validate')->willReturn(true);
+ $this->configWriterMock->expects($this->once())->method('save');
+ $this->configWriterMock->expects($this->once())->method('get')->willReturn(null);
+
+ $processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
+ $processor->setOutput($this->outputMock);
+ $processor->setFinder($finderMock);
+ $processor->setReader($readerMock);
+ $processor->process();
+ }
+
+ /**
+ * @test
+ */
+ public function processWithIfNotSetModifierAlternativeSyntaxAndExistingValue(): void
+ {
+ $finderMock = $this->getMockBuilder(Finder::class)
+ ->onlyMethods(['find'])
+ ->getMock();
+ $finderMock->expects($this->once())->method('find')->willReturn(['abc.yaml']);
+
+ $parseResult = [
+ 'test/config/custom_field_if_not_set' => [
+ 'default' => [
+ 0 => [
+ 'if' => 'not-set',
+ 'default' => 1,
+ ],
+ ],
+ ],
+ ];
+
+ $readerMock = $this->getMockBuilder(YamlReader::class)
+ ->onlyMethods(['parse'])
+ ->getMock();
+ $readerMock->expects($this->once())->method('parse')->willReturn($parseResult);
+
+ $this->scopeValidatorMock->expects($this->once())->method('validate')->willReturn(true);
+ $this->configWriterMock->expects($this->never())->method('save');
+ $this->configWriterMock->expects($this->once())->method('get')->willReturn('existing_value');
+
+ $processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
+ $processor->setOutput($this->outputMock);
+ $processor->setFinder($finderMock);
+ $processor->setReader($readerMock);
+ $processor->process();
+ }
}
diff --git a/docs/config-import.md b/docs/config-import.md
index 4b4e8c4..bbe0bb1 100644
--- a/docs/config-import.md
+++ b/docs/config-import.md
@@ -123,6 +123,28 @@ 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.
+### If-Not-Set Modifier
+
+To conditionally set a configuration value only if it has not been set previously, use the `if-not-set` or `if: not-set` modifier. This is useful for setting initial configuration values for third-party extensions.
+
+For example, this might be the content of your config file:
+
+```
+path/to/config:
+ if-not-set: true
+ default:
+ 0: 1
+```
+
+Or
+
+```
+path/to/config:
+ if: not-set
+ default:
+ 0: 1
+```
+
### 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: