diff --git a/packages/typo3-fractor/config/typo3-12.php b/packages/typo3-fractor/config/typo3-12.php
index 193b4c4d..a40151f0 100644
--- a/packages/typo3-fractor/config/typo3-12.php
+++ b/packages/typo3-fractor/config/typo3-12.php
@@ -8,6 +8,7 @@
use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigrateNullFlagFlexFormFractor;
use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigratePasswordAndSaltedPasswordToPasswordTypeFlexFormFractor;
use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigrateRenderTypeColorpickerToTypeColorFlexFormFractor;
+use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigrateRequiredFlagFlexFormFractor;
use a9f\Typo3Fractor\TYPO3v12\Fluid\AbstractMessageGetSeverityFluidRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@@ -24,4 +25,5 @@
$services->set(MigrateNullFlagFlexFormFractor::class);
$services->set(MigratePasswordAndSaltedPasswordToPasswordTypeFlexFormFractor::class);
$services->set(MigrateRenderTypeColorpickerToTypeColorFlexFormFractor::class);
+ $services->set(MigrateRequiredFlagFlexFormFractor::class);
};
diff --git a/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/Fixtures/Fixture.xml b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/Fixtures/Fixture.xml
new file mode 100644
index 00000000..65450914
--- /dev/null
+++ b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/Fixtures/Fixture.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+ sheetTitle
+ array
+
+
+
+ input
+ required
+
+
+
+
+ input
+ trim,required
+
+
+
+
+ input
+ trim
+
+
+
+
+ input
+
+
+
+
+
+
+
+-----
+
+
+
+
+
+ sheetTitle
+ array
+
+
+
+ input
+ 1
+
+
+
+
+ input
+ trim
+ 1
+
+
+
+
+ input
+ trim
+
+
+
+
+ input
+
+
+
+
+
+
+
diff --git a/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/MigrateRequiredFlagFlexFormFractorTest.php b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/MigrateRequiredFlagFlexFormFractorTest.php
new file mode 100644
index 00000000..c64dfe12
--- /dev/null
+++ b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/MigrateRequiredFlagFlexFormFractorTest.php
@@ -0,0 +1,27 @@
+doTestFile($filePath);
+ }
+
+ public static function provideData(): \Iterator
+ {
+ return self::yieldFilesFromDirectory(__DIR__ . '/Fixtures', '*.xml');
+ }
+
+ public function provideConfigFilePath(): ?string
+ {
+ return __DIR__ . '/config/fractor.php';
+ }
+}
diff --git a/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/config/fractor.php b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/config/fractor.php
new file mode 100644
index 00000000..2e8d397d
--- /dev/null
+++ b/packages/typo3-fractor/rules-tests/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor/config/fractor.php
@@ -0,0 +1,15 @@
+withOptions([
+ XmlProcessorOption::INDENT_CHARACTER => Indent::STYLE_TAB,
+ XmlProcessorOption::INDENT_SIZE => 1,
+ ])
+ ->withRules([MigrateRequiredFlagFlexFormFractor::class]);
diff --git a/packages/typo3-fractor/rules/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor.php b/packages/typo3-fractor/rules/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor.php
new file mode 100644
index 00000000..a8e296d3
--- /dev/null
+++ b/packages/typo3-fractor/rules/TYPO3v12/FlexForm/MigrateRequiredFlagFlexFormFractor.php
@@ -0,0 +1,122 @@
+nodeName === 'config';
+ }
+
+ public function beforeTraversal(File $file, \DOMDocument $rootNode): void
+ {
+ $this->file = $file;
+ $this->domDocument = $rootNode;
+ }
+
+ public function refactor(\DOMNode $node): \DOMNode|int|null
+ {
+ if (! $node instanceof \DOMElement) {
+ return null;
+ }
+
+ if (! $this->hasKey($node, 'eval')) {
+ return null;
+ }
+
+ $evalDomElement = $this->extractDomElementByKey($node, 'eval');
+ if (! $evalDomElement instanceof \DOMElement) {
+ return null;
+ }
+
+ $evalListValue = $evalDomElement->nodeValue;
+ if (! is_string($evalListValue)) {
+ return null;
+ }
+
+ if (! StringUtility::inList($evalListValue, 'required')) {
+ return null;
+ }
+
+ $evalList = ArrayUtility::trimExplode(',', $evalListValue, true);
+
+ // Remove "required" from $evalList
+ $evalList = array_filter($evalList, static fn (string $eval): bool => $eval !== 'required');
+
+ if ($evalList !== []) {
+ // Write back filtered 'eval'
+ $evalDomElement->nodeValue = '';
+ $evalDomElement->appendChild($this->domDocument->createTextNode(implode(',', $evalList)));
+ } elseif ($evalDomElement->parentNode instanceof \DOMElement) {
+ // 'eval' is empty, remove whole configuration
+ $evalDomElement->parentNode->removeChild($evalDomElement);
+ }
+
+ $requiredDomElement = $this->extractDomElementByKey($node, 'required');
+ if (! $requiredDomElement instanceof \DOMElement) {
+ $node->appendChild($this->domDocument->createElement('required', '1'));
+ }
+
+ return $node;
+ }
+
+ public function getRuleDefinition(): RuleDefinition
+ {
+ return new RuleDefinition('Migrate required flag', [new CodeSample(
+ <<<'CODE_SAMPLE'
+
+
+ aTitle
+ array
+
+
+ foo
+
+ trim,required
+
+
+
+
+
+CODE_SAMPLE
+ ,
+ <<<'CODE_SAMPLE'
+
+
+ aTitle
+ array
+
+
+ foo
+
+ trim
+ 1
+
+
+
+
+
+CODE_SAMPLE
+ )]);
+ }
+}