Repository: sabbelasichon/typo3-rector (php)

Issue:
Deprecation: #97126 - TCEforms removed in FlexForm
Deprecation: #97126 - TCEforms removed in FlexForm

https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/12.0/Deprecation-97126-TCEformsRemovedInFlexForm.html
.. include:: /Includes.rst.txt

==================================================
Deprecation: #97126 - TCEforms removed in FlexForm
==================================================

See :issue:`97126`

Description
===========

The `<TCEforms>` tag is no longer necessary (and allowed) in FlexForm
definitions. It was used to wrap TCA configuration and sheet
titles / descriptions. This wrapping must be omitted now.


Impact
======

Not omitting `TCEforms` will trigger a deprecation warning and log a message in
the deprecation log. An automatic migration is in place, which will eventually
be removed in upcoming TYPO3 versions.


Affected Installations
======================

*  All installations making use of FlexForm in their TCA. FlexForm definitions can
   be defined directly in TCA or can be external XML files.

*  Extensions, which extend the YAML configuration for forms with new fields.


Migration
=========

Omit the `<TCEforms>` tag in your FlexForm definition. The underlying
configuration moves one level up.

Before:

.. code-block:: xml

    <T3DataStructure>
        <ROOT>
            <TCEforms>
                <sheetTitle>sheet description 1</sheetTitle>
                <sheetDescription>
                    sheetDescription: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                </sheetDescription>
                <sheetShortDescr>
                    sheetShortDescr: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                </sheetShortDescr>
            </TCEforms>
            <type>array</type>
            <el>
                <input_1>
                    <TCEforms>
                        <label>input_1</label>
                        <config>
                            <type>input</type>
                        </config>
                    </TCEforms>
                </input_1>
            </el>
        </ROOT>
    </T3DataStructure>

After:

.. code-block:: xml

    <T3DataStructure>
        <ROOT>
            <sheetTitle>sheet description 1</sheetTitle>
            <sheetDescription>
                sheetDescription: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </sheetDescription>
            <sheetShortDescr>
                sheetShortDescr: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </sheetShortDescr>
            <type>array</type>
            <el>
                <input_1>
                    <label>input_1</label>
                    <config>
                        <type>input</type>
                    </config>
                </input_1>
            </el>
        </ROOT>
    </T3DataStructure>

Migration for form YAML configuration:

Before:

.. code-block:: yaml

    TYPO3:
      CMS:
        Form:
          prototypes:
            standard:
              finishersDefinition:
                EmailToReceiver:
                  FormEngine:
                    elements:
                      recipients:
                        el:
                          _arrayContainer:
                            el:
                              email:
                                TCEforms:
                                  label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
                                  config:
                                    type: input

After:

.. code-block:: yaml

    TYPO3:
      CMS:
        Form:
          prototypes:
            standard:
              finishersDefinition:
                EmailToReceiver:
                  FormEngine:
                    elements:
                      recipients:
                        el:
                          _arrayContainer:
                            el:
                              email:
                                label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
                                config:
                                  type: input


.. index:: FlexForm, TCA, NotScanned, ext:core


Pull request:
[FEATURE] Add RemoveElementTceFormsYamlRector
Closes #3179

Patch:
diff --git a/config/v12/yaml-120.php b/config/v12/yaml-120.php
new file mode 100644
index 00000000..417cf6d5
--- /dev/null
+++ b/config/v12/yaml-120.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+use Rector\Config\RectorConfig;
+use Ssch\TYPO3Rector\Rector\v12\v0\yaml\RemoveElementTceFormsYamlRector;
+
+return static function (RectorConfig $rectorConfig): void {
+    $rectorConfig->import(__DIR__ . '/../config.php');
+    $rectorConfig->rule(RemoveElementTceFormsYamlRector::class);
+};
diff --git a/src/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector.php b/src/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector.php
new file mode 100644
index 00000000..61575967
--- /dev/null
+++ b/src/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector.php
@@ -0,0 +1,121 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ssch\TYPO3Rector\Rector\v12\v0\yaml;
+
+use Ssch\TYPO3Rector\Contract\FileProcessor\Yaml\YamlRectorInterface;
+use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
+use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
+
+/**
+ * @changelog https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Deprecation-97126-TCEformsRemovedInFlexForm.html
+ * @see \Ssch\TYPO3Rector\Tests\Rector\v12\v0\yaml\RemoveElementTceFormsYamlRector\RemoveElementTceFormsYamlRectorTest
+ */
+final class RemoveElementTceFormsYamlRector implements YamlRectorInterface
+{
+    /**
+     * @param mixed[] $yaml
+     */
+    public function refactor(array $yaml): array
+    {
+        return $this->removeElementTceFormsRecursive($yaml);
+    }
+
+    /**
+     * @codeCoverageIgnore
+     */
+    public function getRuleDefinition(): RuleDefinition
+    {
+        return new RuleDefinition('Remove TCEForms key from all elements in data structure', [new CodeSample(
+            <<<'CODE_SAMPLE'
+TYPO3:
+  CMS:
+    Form:
+      prototypes:
+        standard:
+          finishersDefinition:
+            EmailToReceiver:
+              FormEngine:
+                elements:
+                  recipients:
+                    el:
+                      _arrayContainer:
+                        el:
+                          email:
+                            TCEforms:
+                              label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
+                              config:
+                                type: input
+CODE_SAMPLE
+            ,
+            <<<'CODE_SAMPLE'
+TYPO3:
+  CMS:
+    Form:
+      prototypes:
+        standard:
+          finishersDefinition:
+            EmailToReceiver:
+              FormEngine:
+                elements:
+                  recipients:
+                    el:
+                      _arrayContainer:
+                        el:
+                          email:
+                            label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
+                            config:
+                              type: input
+CODE_SAMPLE
+        )]);
+    }
+
+    /**
+     * Remove "TCEforms" key from all elements in data structure to simplify further parsing.
+     *
+     * Example config:
+     * ['config']['ds']['sheets']['sDEF']['ROOT']['el']['anElement']['TCEforms']['label'] becomes
+     * ['config']['ds']['sheets']['sDEF']['ROOT']['el']['anElement']['label']
+     *
+     * and
+     *
+     * ['ROOT']['TCEforms']['sheetTitle'] becomes
+     * ['ROOT']['sheetTitle']
+     *
+     * @param mixed[] $structure
+     * @return mixed[]
+     * @see https://github.com/TYPO3/typo3/blob/a760989e99aebe714ea1ec8d0f948b84a1d77463/typo3/sysext/core/Classes/Configuration/FlexForm/FlexFormTools.php#L1067-L1100
+     */
+    public function removeElementTceFormsRecursive(array $structure): array
+    {
+        $newStructure = [];
+        foreach ($structure as $key => $value) {
+            if ($key === 'ROOT' && is_array($value) && isset($value['TCEforms'])) {
+                $value = array_merge($value, $value['TCEforms']);
+                unset($value['TCEforms']);
+            }
+
+            if ($key === 'el' && is_array($value)) {
+                $newSubStructure = [];
+                foreach ($value as $subKey => $subValue) {
+                    if (is_array($subValue) && count($subValue) === 1 && isset($subValue['TCEforms'])) {
+                        $newSubStructure[$subKey] = $subValue['TCEforms'];
+                    } else {
+                        $newSubStructure[$subKey] = $subValue;
+                    }
+                }
+
+                $value = $newSubStructure;
+            }
+
+            if (is_array($value)) {
+                $value = $this->removeElementTceFormsRecursive($value);
+            }
+
+            $newStructure[$key] = $value;
+        }
+
+        return $newStructure;
+    }
+}


Test patch:
diff --git a/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/Fixture/fixture.yaml.inc b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/Fixture/fixture.yaml.inc
new file mode 100644
index 00000000..1f4928f9
--- /dev/null
+++ b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/Fixture/fixture.yaml.inc
@@ -0,0 +1,36 @@
+TYPO3:
+  CMS:
+    Form:
+      prototypes:
+        standard:
+          finishersDefinition:
+            EmailToReceiver:
+              FormEngine:
+                elements:
+                  recipients:
+                    el:
+                      _arrayContainer:
+                        el:
+                          email:
+                            TCEforms:
+                              label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
+                              config:
+                                type: input
+-----
+TYPO3:
+  CMS:
+    Form:
+      prototypes:
+        standard:
+          finishersDefinition:
+            EmailToReceiver:
+              FormEngine:
+                elements:
+                  recipients:
+                    el:
+                      _arrayContainer:
+                        el:
+                          email:
+                            label: tt_content.finishersDefinition.EmailToSender.recipients.email.label
+                            config:
+                              type: input
diff --git a/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/RemoveElementTceFormsYamlRectorTest.php b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/RemoveElementTceFormsYamlRectorTest.php
new file mode 100644
index 00000000..36e5cb27
--- /dev/null
+++ b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/RemoveElementTceFormsYamlRectorTest.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ssch\TYPO3Rector\Tests\Rector\v12\v0\yaml\RemoveElementTceFormsYamlRector;
+
+use Iterator;
+use Rector\Testing\PHPUnit\AbstractRectorTestCase;
+
+final class RemoveElementTceFormsYamlRectorTest extends AbstractRectorTestCase
+{
+    /**
+     * @dataProvider provideData()
+     */
+    public function test(string $filePath): void
+    {
+        $this->doTestFile($filePath);
+    }
+
+    /**
+     * @return Iterator<array<string>>
+     */
+    public function provideData(): Iterator
+    {
+        return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.yaml.inc');
+    }
+
+    public function provideConfigFilePath(): string
+    {
+        return __DIR__ . '/config/configured_rule.php';
+    }
+}
diff --git a/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/config/configured_rule.php b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/config/configured_rule.php
new file mode 100644
index 00000000..c442a90a
--- /dev/null
+++ b/tests/Rector/v12/v0/yaml/RemoveElementTceFormsYamlRector/config/configured_rule.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+use Rector\Config\RectorConfig;
+use Ssch\TYPO3Rector\Rector\v12\v0\yaml\RemoveElementTceFormsYamlRector;
+
+return static function (RectorConfig $rectorConfig): void {
+    $rectorConfig->import(__DIR__ . '/../../../../../../../config/config_test.php');
+    $rectorConfig->rule(RemoveElementTceFormsYamlRector::class);
+};