38fa5842a919b203c78b8a649b88c10e289b40e9
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Factory / ArrayFormFactory.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Form\Domain\Factory;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It originated from the Neos.Form package (www.neos.io)
9 *
10 * It is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License, either version 2
12 * of the License, or any later version.
13 *
14 * For the full copyright and license information, please read the
15 * LICENSE.txt file that was distributed with this source code.
16 *
17 * The TYPO3 project - inspiring people to share!
18 */
19
20 use TYPO3\CMS\Core\Utility\ArrayUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Extbase\Object\ObjectManager;
23 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
24 use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException;
25 use TYPO3\CMS\Form\Domain\Exception\UnknownCompositRenderableException;
26 use TYPO3\CMS\Form\Domain\Model\FormDefinition;
27 use TYPO3\CMS\Form\Domain\Model\FormElements\AbstractSection;
28 use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface;
29
30 /**
31 * A factory that creates a FormDefinition from an array
32 *
33 * Scope: frontend / backend
34 */
35 class ArrayFormFactory extends AbstractFormFactory
36 {
37
38 /**
39 * Build a form definition, depending on some configuration.
40 *
41 * @param array $configuration
42 * @param string $prototypeName
43 * @return FormDefinition
44 * @internal
45 */
46 public function build(array $configuration, string $prototypeName = null): FormDefinition
47 {
48 if (empty($prototypeName)) {
49 $prototypeName = $configuration['prototypeName'] ?? 'standard';
50 }
51 $persistenceIdentifier = $configuration['persistenceIdentifier'] ?? null;
52
53 $configuration = $this->transformFormDefinitionWithImportsForFormFramework($configuration);
54 unset($configuration['imports']);
55 $configuration = ArrayUtility::convertBooleanStringsToBooleanRecursive($configuration);
56 $configuration = ArrayUtility::removeNullValuesRecursive($configuration);
57
58 $prototypeConfiguration = GeneralUtility::makeInstance(ObjectManager::class)
59 ->get(ConfigurationService::class)
60 ->getPrototypeConfiguration($prototypeName);
61
62 $form = GeneralUtility::makeInstance(ObjectManager::class)->get(
63 FormDefinition::class,
64 $configuration['identifier'],
65 $prototypeConfiguration,
66 'Form',
67 $persistenceIdentifier
68 );
69 if (isset($configuration['renderables'])) {
70 foreach ($configuration['renderables'] as $pageConfiguration) {
71 $this->addNestedRenderable($pageConfiguration, $form);
72 }
73 }
74
75 unset($configuration['persistenceIdentifier']);
76 unset($configuration['prototypeName']);
77 unset($configuration['renderables']);
78 unset($configuration['type']);
79 unset($configuration['identifier']);
80 $form->setOptions($configuration);
81
82 $this->triggerFormBuildingFinished($form);
83
84 return $form;
85 }
86
87 /**
88 * Add form elements to the $parentRenderable
89 *
90 * @param array $nestedRenderableConfiguration
91 * @param CompositeRenderableInterface $parentRenderable
92 * @return mixed
93 * @throws IdentifierNotValidException
94 * @throws UnknownCompositRenderableException
95 */
96 protected function addNestedRenderable(array $nestedRenderableConfiguration, CompositeRenderableInterface $parentRenderable)
97 {
98 if (!isset($nestedRenderableConfiguration['identifier'])) {
99 throw new IdentifierNotValidException('Identifier not set.', 1329289436);
100 }
101 if ($parentRenderable instanceof FormDefinition) {
102 $renderable = $parentRenderable->createPage($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']);
103 } elseif ($parentRenderable instanceof AbstractSection) {
104 $renderable = $parentRenderable->createElement($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']);
105 } else {
106 throw new UnknownCompositRenderableException('Unknown composit renderable "' . get_class($parentRenderable) . '"', 1479593622);
107 }
108
109 if (isset($nestedRenderableConfiguration['renderables']) && is_array($nestedRenderableConfiguration['renderables'])) {
110 $childRenderables = $nestedRenderableConfiguration['renderables'];
111 } else {
112 $childRenderables = [];
113 }
114
115 unset($nestedRenderableConfiguration['type']);
116 unset($nestedRenderableConfiguration['identifier']);
117 unset($nestedRenderableConfiguration['renderables']);
118
119 $renderable->setOptions($nestedRenderableConfiguration);
120
121 foreach ($childRenderables as $elementConfiguration) {
122 $this->addNestedRenderable($elementConfiguration, $renderable);
123 }
124
125 return $renderable;
126 }
127
128 /**
129 * @param array $array
130 * @return array
131 * @internal
132 */
133 protected function transformFormDefinitionWithImportsForFormFramework(array $array): array
134 {
135 $result = $array;
136 foreach ($result as $key => $value) {
137 if (is_array($value)) {
138 if (
139 $key === 'renderables'
140 || $key === 'validators'
141 || $key === 'finishers'
142 ) {
143 // sort by "sorting"
144 usort($value, function ($a, $b) {
145 return (float)$a['sorting'] - (float)$b['sorting'];
146 });
147
148 foreach ($value as $itemKey => $item) {
149 unset($value[$itemKey]['sorting']);
150 }
151 }
152 $result[$key] = $this->transformFormDefinitionWithImportsForFormFramework($value);
153 }
154 }
155 return $result;
156 }
157 }