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