‪TYPO3CMS  9.5
FormDataCompiler.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
24 {
30  protected ‪$formDataGroup;
31 
39  ];
40 
47  {
48  $this->formDataGroup = ‪$formDataGroup;
49  }
50 
60  public function ‪compile(array $initialData)
61  {
62  $result = $this->‪initializeResultArray();
63 
64  // There must be only keys that actually exist in result data.
65  $keysNotInResult = array_diff(array_keys($initialData), array_keys($result));
66  if (!empty($keysNotInResult)) {
67  throw new \InvalidArgumentException(
68  'Array keys ' . implode(',', $keysNotInResult) . ' do not exist in result array and can not be set',
69  1440601540
70  );
71  }
72 
73  foreach ($initialData as $dataKey => $dataValue) {
74  if ($dataKey === 'command') {
75  // Sanitize $command
76  if ($dataValue !== 'edit' && $dataValue !== 'new') {
77  throw new \InvalidArgumentException('Command must be either "edit" or "new"', 1437653136);
78  }
79  }
80  if ($dataKey === 'tableName') {
81  // Sanitize $tableName
82  if (empty($dataValue)) {
83  throw new \InvalidArgumentException('No $tableName given', 1437654409);
84  }
85  }
86  if ($dataKey === 'vanillaUid') {
87  if (!‪MathUtility::canBeInterpretedAsInteger($dataValue) && strpos($dataValue, 'NEW') !== 0) {
88  throw new \InvalidArgumentException('$vanillaUid is not an integer or "NEW..." string ID', 1437654247);
89  }
90  if (isset($initialData['command']) && $initialData['command'] === 'edit' && $dataValue < 0) {
91  throw new \InvalidArgumentException('Negative $vanillaUid is not supported with $command="edit', 1437654332);
92  }
93  }
94  $result[$dataKey] = $dataValue;
95  }
96 
97  // Call the data group provider but take care it does not add or remove result keys
98  // This is basically a safety measure against data providers colliding with our array "contract"
99  $resultKeysBeforeFormDataGroup = array_keys($result);
100 
101  $result = $this->formDataGroup->compile($result);
102 
103  if (!is_array($result)) {
104  throw new \UnexpectedValueException(
105  'Data group provider must return array',
106  1446664764
107  );
108  }
109 
110  if (!empty($result['renderData'])) {
111  throw new \RuntimeException(
112  'Array \'renderData\' not empty. Data providers must not add data here',
113  1485201279
114  );
115  }
116 
117  $resultKeysAfterFormDataGroup = array_keys($result);
118 
119  if ($resultKeysAfterFormDataGroup !== $resultKeysBeforeFormDataGroup) {
120  throw new \UnexpectedValueException(
121  'Data group provider must not change result key list',
122  1438079402
123  );
124  }
125 
126  // Remove some data elements form result that are data provider internal and should
127  // not be exposed to calling object.
128  foreach ($this->removeKeysFromFinalResultArray as $key) {
129  unset($result[$key]);
130  }
131 
132  return $result;
133  }
134 
138  protected function ‪initializeResultArray()
139  {
140  return [
141  // Either "edit" or "new"
142  'command' => '',
143  // Table name of the handled row
144  'tableName' => '',
145  // Not changed uid of the record, meaning of value depends on context (new / edit)
146  // * If $command is "edit"
147  // ** $vanillaUid is a positive integer > 0 pointing to the record in the table
148  // * If $command is "new":
149  // ** If $vanillaUid > 0, it is the uid of a page the record should be added at
150  // ** If $vanillaUid < 0, it is the uid of a record in the same table after which the new record should be added
151  // ** If $vanillaUid = 0, a new record is added on page 0
152  // ** If $vanillaUid = "NEW..." Id of a parent page record if an inline child is added to a not yet persisted page
153  'vanillaUid' => 0,
154  // Url to return to
155  'returnUrl' => null,
156  // Title of the handled record.
157  'recordTitle' => '',
158  // Parent page record is either the full row of the parent page the record is located at or should
159  // be added to, or it is NULL, if a record is added or edited below the root page node.
160  'parentPageRow' => null,
161  // If a translated page is handled, the page row of the default language (the page against all page checks
162  // are made) is set here
163  'defaultLanguagePageRow' => null,
164  // Holds the "neighbor" row if incoming vanillaUid is negative and record creation is relative to a row of the same table.
165  'neighborRow' => null,
166  // For "new" this is the fully initialized row with defaults
167  // The database row. For "edit" fixVersioningPid() was applied already.
168  // @todo: rename to valueStructure or handledData or similar
169  'databaseRow' => [],
170  // The "effective" page uid we're working on. This is the uid of a page if a page is edited, or the uid
171  // of the parent page if a page or other record is added, or 0 if a record is added or edited below root node.
172  'effectivePid' => 0,
173  // Rootline of page the record that is handled is located at as created by BackendUtility::BEgetRootline()
174  'rootline' => [],
175  // The resolved SiteInterface object of the page or page given record is located on.
176  'site' => null,
177  // For "edit", this is the permission bitmask of the page that is edited, or of the page a record is located at
178  // For "new", this is the permission bitmask of the page the record is added to
179  // @todo: Remove if not needed on a lower level
180  'userPermissionOnPage' => 0,
181  // Full user TsConfig
182  'userTsConfig' => [],
183  // Full page TSConfig of the page that is edited or of the parent page if a record is added.
184  // This includes any defaultPageTSconfig and is merged with user TsConfig page. section. After type
185  // of handled record was determined, record type specific settings [TCEFORM.][tableName.][field.][types.][type.]
186  // are merged into [TCEFORM.][tableName.][field.]. Array keys still contain the concatenation dots.
187  'pageTsConfig' => [],
188  // Not changed TCA of parent page row if record is edited or added below a page and not root node
189  'vanillaParentPageTca' => null,
190  // List of available system languages. Array key is the system language uid, value array
191  // contains details of the record, with iso code resolved. Key is the sys_language_uid uid.
192  'systemLanguageRows' => [],
193  // If the page that is handled has "page_language_overlay" records (page has localizations in
194  // different languages), then this array holds those rows.
195  'pageLanguageOverlayRows' => [],
196  // If the handled row is a localized row, this entry hold the default language row array
197  'defaultLanguageRow' => null,
198  // If the handled row is a localived row and $TCA[<tableName>]['ctrl']['translationSource'] is configured,
199  // This entry holds the row of the language source record.
200  'sourceLanguageRow' => null,
201  // If the handled row is a localized row and a transOrigDiffSourceField is defined, this
202  // is the unserialized version of it. The diff source field is basically a shadow version
203  // of the default language record at the time when the language overlay record was created.
204  // This is used later to compare the default record with this content to show a "diff" if
205  // the default language record changed meanwhile.
206  'defaultLanguageDiffRow' => null,
207  // With userTS options.additionalPreviewLanguages set, field values of additional languages
208  // can be shown. This array holds those additional language records, Array key is sys_language_uid.
209  'additionalLanguageRows' => [],
210  // The tca record type value of the record. Forced to string, there can be "named" type values.
211  'recordTypeValue' => '',
212  // TCA of table with processed fields. After processing, this array contains merged and resolved
213  // array data, items were resolved, only used types are set, renderTypes are set.
214  'processedTca' => [],
215  // List of columns to be processed by data provider. Array value is the column name.
216  'columnsToProcess' => [],
217  // If set to TRUE, no wizards are calculated and rendered later
218  'disabledWizards' => false,
219 
220  // Flex form field data handling is done in a separated FormDataCompiler instance. The full databaseRow
221  // of the record this flex form is embedded in is transferred in case features like single fields
222  // itemsProcFunc need to have this data at hand to do their job.
223  'flexParentDatabaseRow' => [],
224  // If not empty, it tells the TcaFlexProcess data provider to not calculate existing flex fields and
225  // existing flex container sections, but to instead prepare field values and the data structure TCA
226  // for a new container section. This is used by FormFlexAjaxController, the array contains details
227  // which container of which flex field should be created.
228  'flexSectionContainerPreparation' => [],
229 
230  // If true, TcaSelectTreeItems data provider will compile tree items. This is false by default since
231  // on opening a record items are not calculated but are fetch in an ajax request, see FormSelectTreeAjaxController.
232  'selectTreeCompileItems' => false,
233 
234  // BackendUser->uc['inlineView'] - This array holds status of expand / collapsed inline items
235  // This array is "flat", an inline structure with parent uid 1 having firstChild uid 2 having secondChild uid 3
236  // firstChild and secondChild are not nested. If an uid is set it means "record is expanded", example:
237  // 'parent' => [
238  // 1 => [
239  // 'firstChild' => [ 2 ], // record 2 of firstChild table is open in inline context to parent 1
240  // 'secondChild' => [ 3 ], // record 3 of secondChild table is open in inline context to parent 1
241  // ],
242  // ]
243  'inlineExpandCollapseStateArray' => [],
244  // The "entry" pid for inline records. Nested inline records can potentially hang around on different
245  // pid's, but the entry pid is needed for AJAX calls, so that they would know where the action takes
246  // place on the page structure. This is usually an int, but can be a "NEW..." string if an inline relation
247  // is added to a currently being created page.
248  'inlineFirstPid' => null,
249  // The "config" section of an inline parent, prepared and sanitized by TcaInlineConfiguration provider
250  'inlineParentConfig' => [],
251  // Flag that is enabled if a records is child of an inline parent
252  'isInlineChild' => false,
253  // Flag if an inline child is expanded so that additional fields need to be rendered
254  'isInlineChildExpanded' => false,
255  // Flag if the inline is in an ajax context that wants to expand the element
256  'isInlineAjaxOpeningContext' => false,
257  // Uid of the direct parent of the inline element. Handled as string since it may be a "NEW123" string
258  'inlineParentUid' => '',
259  // Table name of the direct parent of the inline element
260  'inlineParentTableName' => '',
261  // Field name of the direct parent of the inline element
262  'inlineParentFieldName' => '',
263  // Uid of the top most parent element. Handled as string since it may be a "NEW123" string
264  'inlineTopMostParentUid' => '',
265  // Table name of the top most parent element
266  'inlineTopMostParentTableName' => '',
267  // Field name of the top most parent element
268  'inlineTopMostParentFieldName' => '',
269 
270  // If is on symetric side of an inline child parent reference.
271  // symmetric side can be achieved in case of an mm relation to the same table. If record A has a relation
272  // to record B, the symmetric side is set in case that record B gets edited.
273  // Record A (table1) <=> mm <=> Record B (table1)
274  'isOnSymmetricSide' => false,
275 
276  // Uid of a "child-child" if a new record of an intermediate table is compiled to an existing child. This
277  // happens if foreign_selector in parent inline config is set. It will be used by default database row
278  // data providers to set this as value for the foreign_selector field on the intermediate table. One use
279  // case is FAL, where for instance a tt_content parent adds relation to an existing sys_file record and
280  // should set the uid of the existing sys_file record as uid_local - the foreign_selector of this inline
281  // configuration - of the new intermediate sys_file_reference record. Data provider that are called later
282  // will then use this relation to resolve for instance input placeholder relation values.
283  'inlineChildChildUid' => null,
284  // Inline scenario: A localized parent record is handled, so inline parents can have localized children.
285  // This value is set to TRUE if this array represents a default language
286  // child record that was not yet localized.
287  'isInlineDefaultLanguageRecordInLocalizedParentContext' => false,
288  // If set, inline children will be resolved. This is set to FALSE in inline ajax context where new children
289  // are created and existing children don't matter much.
290  'inlineResolveExistingChildren' => true,
291  // @todo - for input placeholder inline to suppress an infinite loop, this *may* become obsolete if
292  // @todo compilation of certain fields is possible
293  'inlineCompileExistingChildren' => true,
294 
295  // @todo: keys below must be handled / further defined
296  'elementBaseName' => '',
297  'tabAndInlineStack' => [],
298  'inlineData' => [],
299  'inlineStructure' => [],
300 
301  // This array of fields will be set as hidden-fields instead of rendered normally!
302  // This is used by EditDocumentController to force some field values if set as "overrideVals" in _GP
303  'overrideValues' => [],
304  // Default values for fields. This is for example used in DatabaseRowInitializeNew data provider to set
305  // fields to specific values if new records are created. Values are often carried around as "defVals" GET
306  // parameters and hand over by controllers to FormEngine using this array. Array structure is
307  // ['aTableName']['aFieldName'] = 'aValue'.
308  'defaultValues' => [],
309 
310  // This array must NOT be set / manipulated by data providers but is instead used by the render part
311  // of FormEngine to add runtime data. Containers and elements add data here which is given to
312  // sub-containers, elements, controls and wizards.
313  'renderData' => [],
314 
315  // A place for non-core, additional, custom data providers to add data. If a data provider needs to add
316  // additional data to the data array that doesn't fit elsewhere, it can place it here to use it in the
317  // render part again. Data in here should be namespaced in a way that it does not collide with other
318  // data providers adding further data here. Using the extension key as array key could be a good idea.
319  'customData' => [],
320  ];
321  }
322 }
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Backend\Form
Definition: AbstractNode.php:3
‪TYPO3\CMS\Backend\Form\FormDataCompiler\compile
‪array compile(array $initialData)
Definition: FormDataCompiler.php:58
‪TYPO3\CMS\Backend\Form\FormDataCompiler\__construct
‪__construct(FormDataGroupInterface $formDataGroup)
Definition: FormDataCompiler.php:44
‪TYPO3\CMS\Backend\Form\FormDataCompiler\$formDataGroup
‪FormDataGroupInterface $formDataGroup
Definition: FormDataCompiler.php:29
‪TYPO3\CMS\Backend\Form\FormDataCompiler\$removeKeysFromFinalResultArray
‪array $removeKeysFromFinalResultArray
Definition: FormDataCompiler.php:36
‪TYPO3\CMS\Backend\Form\FormDataCompiler\initializeResultArray
‪array initializeResultArray()
Definition: FormDataCompiler.php:136
‪TYPO3\CMS\Backend\Form\FormDataGroupInterface
Definition: FormDataGroupInterface.php:22
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪TYPO3\CMS\Backend\Form\FormDataCompiler
Definition: FormDataCompiler.php:24