‪TYPO3CMS  ‪main
DefaultTcaSchema.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Platforms\SQLitePlatform as DoctrineSQLitePlatform;
21 use Doctrine\DBAL\Schema\Table;
22 use Doctrine\DBAL\Types\Types;
28 
43 {
53  public function enrich(array ‪$tables): array
54  {
55  // Sanity check to ensure all TCA tables are already defined in incoming table list.
56  // This prevents a misuse, calling code needs to ensure there is at least an empty
57  // table object (no columns) for all TCA tables.
58  $tableNamesFromTca = array_keys(‪$GLOBALS['TCA']);
59  $existingTableNames = array_keys(‪$tables);
60  foreach ($tableNamesFromTca as $tableName) {
61  if (!in_array($tableName, $existingTableNames, true)) {
62  throw new \RuntimeException(
63  'Table name ' . $tableName . ' does not exist in incoming table list',
64  1696424993
65  );
66  }
67  }
68 
70  ‪$tables = $this->enrichSingleTableFieldsFromTcaColumns(‪$tables);
71  return $this->enrichMmTables(‪$tables);
72  }
73 
80  protected function ‪enrichSingleTableFieldsFromTcaCtrl(array ‪$tables): array
81  {
82  foreach (‪$GLOBALS['TCA'] as $tableName => $tableDefinition) {
83  // uid column and primary key if uid is not defined
84  if (!$this->‪isColumnDefinedForTable($tables, $tableName, 'uid')) {
85  ‪$tables[$tableName]->addColumn(
86  $this->‪quote('uid'),
87  Types::INTEGER,
88  [
89  'notnull' => true,
90  'unsigned' => true,
91  'autoincrement' => true,
92  ]
93  );
94  ‪$tables[$tableName]->setPrimaryKey(['uid']);
95  }
96 
97  // pid column and prepare parent key if pid is not defined
99  if (!$this->‪isColumnDefinedForTable($tables, $tableName, 'pid')) {
100  $options = [
101  'default' => 0,
102  'notnull' => true,
103  'unsigned' => true,
104  ];
105  ‪$tables[$tableName]->addColumn($this->‪quote('pid'), Types::INTEGER, $options);
106  ‪$pidColumnAdded = true;
107  }
108 
109  // tstamp column
110  // not converted to bigint because already unsigned and date before 1970 not needed
111  if (!empty($tableDefinition['ctrl']['tstamp'])
112  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['tstamp'])
113  ) {
114  ‪$tables[$tableName]->addColumn(
115  $this->‪quote($tableDefinition['ctrl']['tstamp']),
116  Types::INTEGER,
117  [
118  'default' => 0,
119  'notnull' => true,
120  'unsigned' => true,
121  ]
122  );
123  }
124 
125  // crdate column
126  if (!empty($tableDefinition['ctrl']['crdate'])
127  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['crdate'])
128  ) {
129  ‪$tables[$tableName]->addColumn(
130  $this->‪quote($tableDefinition['ctrl']['crdate']),
131  Types::INTEGER,
132  [
133  'default' => 0,
134  'notnull' => true,
135  'unsigned' => true,
136  ]
137  );
138  }
139 
140  // deleted column - soft delete
141  if (!empty($tableDefinition['ctrl']['delete'])
142  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['delete'])
143  ) {
144  ‪$tables[$tableName]->addColumn(
145  $this->‪quote($tableDefinition['ctrl']['delete']),
146  Types::SMALLINT,
147  [
148  'default' => 0,
149  'notnull' => true,
150  'unsigned' => true,
151  ]
152  );
153  }
154 
155  // disabled column
156  if (!empty($tableDefinition['ctrl']['enablecolumns']['disabled'])
157  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['disabled'])
158  ) {
159  ‪$tables[$tableName]->addColumn(
160  $this->‪quote($tableDefinition['ctrl']['enablecolumns']['disabled']),
161  Types::SMALLINT,
162  [
163  'default' => 0,
164  'notnull' => true,
165  'unsigned' => true,
166  ]
167  );
168  }
169 
170  // starttime column
171  // not converted to bigint because already unsigned and date before 1970 not needed
172  if (!empty($tableDefinition['ctrl']['enablecolumns']['starttime'])
173  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['starttime'])
174  ) {
175  ‪$tables[$tableName]->addColumn(
176  $this->‪quote($tableDefinition['ctrl']['enablecolumns']['starttime']),
177  Types::INTEGER,
178  [
179  'default' => 0,
180  'notnull' => true,
181  'unsigned' => true,
182  ]
183  );
184  }
185 
186  // endtime column
187  // not converted to bigint because already unsigned and date before 1970 not needed
188  if (!empty($tableDefinition['ctrl']['enablecolumns']['endtime'])
189  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['endtime'])
190  ) {
191  ‪$tables[$tableName]->addColumn(
192  $this->‪quote($tableDefinition['ctrl']['enablecolumns']['endtime']),
193  Types::INTEGER,
194  [
195  'default' => 0,
196  'notnull' => true,
197  'unsigned' => true,
198  ]
199  );
200  }
201 
202  // fe_group column
203  if (!empty($tableDefinition['ctrl']['enablecolumns']['fe_group'])
204  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['fe_group'])
205  ) {
206  ‪$tables[$tableName]->addColumn(
207  $this->‪quote($tableDefinition['ctrl']['enablecolumns']['fe_group']),
208  Types::STRING,
209  [
210  'default' => '0',
211  'notnull' => true,
212  'length' => 255,
213  ]
214  );
215  }
216 
217  // sorting column
218  if (!empty($tableDefinition['ctrl']['sortby'])
219  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['sortby'])
220  ) {
221  ‪$tables[$tableName]->addColumn(
222  $this->‪quote($tableDefinition['ctrl']['sortby']),
223  Types::INTEGER,
224  [
225  'default' => 0,
226  'notnull' => true,
227  'unsigned' => false,
228  ]
229  );
230  }
231 
232  // index on pid column and maybe others - only if pid has not been defined via ext_tables.sql before
233  if (‪$pidColumnAdded && !$this->‪isIndexDefinedForTable($tables, $tableName, 'parent')) {
234  $parentIndexFields = ['pid'];
235  if (!empty($tableDefinition['ctrl']['delete'])) {
236  $parentIndexFields[] = (string)$tableDefinition['ctrl']['delete'];
237  }
238  if (!empty($tableDefinition['ctrl']['enablecolumns']['disabled'])) {
239  $parentIndexFields[] = (string)$tableDefinition['ctrl']['enablecolumns']['disabled'];
240  }
241  ‪$tables[$tableName]->addIndex($parentIndexFields, 'parent');
242  }
243 
244  // description column
245  if (!empty($tableDefinition['ctrl']['descriptionColumn'])
246  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['descriptionColumn'])
247  ) {
248  ‪$tables[$tableName]->addColumn(
249  $this->‪quote($tableDefinition['ctrl']['descriptionColumn']),
250  Types::TEXT,
251  [
252  'notnull' => false,
253  'length' => 65535,
254  ]
255  );
256  }
257 
258  // editlock column
259  if (!empty($tableDefinition['ctrl']['editlock'])
260  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['editlock'])
261  ) {
262  ‪$tables[$tableName]->addColumn(
263  $this->‪quote($tableDefinition['ctrl']['editlock']),
264  Types::SMALLINT,
265  [
266  'default' => 0,
267  'notnull' => true,
268  'unsigned' => true,
269  ]
270  );
271  }
272 
273  // sys_language_uid column
274  if (!empty($tableDefinition['ctrl']['languageField'])
275  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['languageField'])
276  ) {
277  ‪$tables[$tableName]->addColumn(
278  $this->‪quote((string)$tableDefinition['ctrl']['languageField']),
279  Types::INTEGER,
280  [
281  'default' => 0,
282  'notnull' => true,
283  'unsigned' => false,
284  ]
285  );
286  }
287 
288  // l10n_parent column
289  if (!empty($tableDefinition['ctrl']['languageField'])
290  && !empty($tableDefinition['ctrl']['transOrigPointerField'])
291  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['transOrigPointerField'])
292  ) {
293  ‪$tables[$tableName]->addColumn(
294  $this->‪quote((string)$tableDefinition['ctrl']['transOrigPointerField']),
295  Types::INTEGER,
296  [
297  'default' => 0,
298  'notnull' => true,
299  'unsigned' => true,
300  ]
301  );
302  }
303 
304  // l10n_source column
305  if (!empty($tableDefinition['ctrl']['languageField'])
306  && !empty($tableDefinition['ctrl']['translationSource'])
307  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['translationSource'])
308  ) {
309  ‪$tables[$tableName]->addColumn(
310  $this->‪quote((string)$tableDefinition['ctrl']['translationSource']),
311  Types::INTEGER,
312  [
313  'default' => 0,
314  'notnull' => true,
315  'unsigned' => true,
316  ]
317  );
318  ‪$tables[$tableName]->addIndex([$tableDefinition['ctrl']['translationSource']], 'translation_source');
319  }
320 
321  // l10n_state column
322  if (!empty($tableDefinition['ctrl']['languageField'])
323  && !empty($tableDefinition['ctrl']['transOrigPointerField'])
324  && !$this->‪isColumnDefinedForTable($tables, $tableName, 'l10n_state')
325  ) {
326  ‪$tables[$tableName]->addColumn(
327  $this->‪quote('l10n_state'),
328  Types::TEXT,
329  [
330  'notnull' => false,
331  'length' => 65535,
332  ]
333  );
334  }
335 
336  // t3_origuid column
337  if (!empty($tableDefinition['ctrl']['origUid'])
338  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['origUid'])
339  ) {
340  ‪$tables[$tableName]->addColumn(
341  $this->‪quote($tableDefinition['ctrl']['origUid']),
342  Types::INTEGER,
343  [
344  'default' => 0,
345  'notnull' => true,
346  'unsigned' => true,
347  ]
348  );
349  }
350 
351  // l18n_diffsource column
352  if (!empty($tableDefinition['ctrl']['transOrigDiffSourceField'])
353  && !$this->‪isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['transOrigDiffSourceField'])
354  ) {
355  ‪$tables[$tableName]->addColumn(
356  $this->‪quote($tableDefinition['ctrl']['transOrigDiffSourceField']),
357  Types::BLOB,
358  [
359  // mediumblob (16MB) on mysql
360  'length' => 16777215,
361  'notnull' => false,
362  ]
363  );
364  }
365 
366  // workspaces t3ver_oid column
367  if (!empty($tableDefinition['ctrl']['versioningWS'])
368  && (bool)$tableDefinition['ctrl']['versioningWS'] === true
369  && !$this->‪isColumnDefinedForTable($tables, $tableName, 't3ver_oid')
370  ) {
371  ‪$tables[$tableName]->addColumn(
372  $this->‪quote('t3ver_oid'),
373  Types::INTEGER,
374  [
375  'default' => 0,
376  'notnull' => true,
377  'unsigned' => true,
378  ]
379  );
380  }
381 
382  // workspaces t3ver_wsid column
383  if (!empty($tableDefinition['ctrl']['versioningWS'])
384  && (bool)$tableDefinition['ctrl']['versioningWS'] === true
385  && !$this->‪isColumnDefinedForTable($tables, $tableName, 't3ver_wsid')
386  ) {
387  ‪$tables[$tableName]->addColumn(
388  $this->‪quote('t3ver_wsid'),
389  Types::INTEGER,
390  [
391  'default' => 0,
392  'notnull' => true,
393  'unsigned' => true,
394  ]
395  );
396  }
397 
398  // workspaces t3ver_state column
399  if (!empty($tableDefinition['ctrl']['versioningWS'])
400  && (bool)$tableDefinition['ctrl']['versioningWS'] === true
401  && !$this->‪isColumnDefinedForTable($tables, $tableName, 't3ver_state')
402  ) {
403  ‪$tables[$tableName]->addColumn(
404  $this->‪quote('t3ver_state'),
405  Types::SMALLINT,
406  [
407  'default' => 0,
408  'notnull' => true,
409  'unsigned' => false,
410  ]
411  );
412  }
413 
414  // workspaces t3ver_stage column
415  if (!empty($tableDefinition['ctrl']['versioningWS'])
416  && (bool)$tableDefinition['ctrl']['versioningWS'] === true
417  && !$this->‪isColumnDefinedForTable($tables, $tableName, 't3ver_stage')
418  ) {
419  ‪$tables[$tableName]->addColumn(
420  $this->‪quote('t3ver_stage'),
421  Types::INTEGER,
422  [
423  'default' => 0,
424  'notnull' => true,
425  'unsigned' => false,
426  ]
427  );
428  }
429 
430  // workspaces index on t3ver_oid and t3ver_wsid fields
431  if (!empty($tableDefinition['ctrl']['versioningWS'])
432  && (bool)$tableDefinition['ctrl']['versioningWS'] === true
433  && !$this->‪isIndexDefinedForTable($tables, $tableName, 't3ver_oid')
434  ) {
435  ‪$tables[$tableName]->addIndex(['t3ver_oid', 't3ver_wsid'], 't3ver_oid');
436  }
437  }
438 
439  return ‪$tables;
440  }
441 
448  protected function enrichSingleTableFieldsFromTcaColumns(array ‪$tables): array
449  {
450  foreach (‪$GLOBALS['TCA'] as $tableName => $tableDefinition) {
451  // In the following, columns for TCA fields with a dedicated TCA type are
452  // added. In the unlikely case that no columns exist, we can skip the table.
453  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
454  continue;
455  }
456 
457  // Add category fields for all tables, defining category columns (TCA type=category)
458  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
459  if ((string)($fieldConfig['config']['type'] ?? '') !== 'category'
460  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
461  ) {
462  continue;
463  }
464 
465  if (($fieldConfig['config']['relationship'] ?? '') === 'oneToMany') {
466  ‪$tables[$tableName]->addColumn(
467  $this->‪quote($fieldName),
468  Types::TEXT,
469  [
470  'notnull' => false,
471  ]
472  );
473  } else {
474  ‪$tables[$tableName]->addColumn(
475  $this->‪quote($fieldName),
476  Types::INTEGER,
477  [
478  'default' => 0,
479  'notnull' => true,
480  'unsigned' => true,
481  ]
482  );
483  }
484  }
485 
486  // Add datetime fields for all tables, defining datetime columns (TCA type=datetime), except
487  // those columns, which had already been added due to definition in "ctrl", e.g. "starttime".
488  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
489  if ((string)($fieldConfig['config']['type'] ?? '') !== 'datetime'
490  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
491  ) {
492  continue;
493  }
494 
495  if (in_array($fieldConfig['config']['dbType'] ?? '', ‪QueryHelper::getDateTimeTypes(), true)) {
496  ‪$tables[$tableName]->addColumn(
497  $this->‪quote($fieldName),
498  $fieldConfig['config']['dbType'],
499  [
500  'notnull' => false,
501  ]
502  );
503  } else {
504  // int unsigned: from 1970 to 2106.
505  // int signed: from 1901 to 2038.
506  // bigint unsigned/signed: from whenever to whenever
507  //
508  // Anything like crdate,tstamp,starttime,endtime is good with
509  // "int unsigned" and can survive the 2038 apocalypse (until 2106).
510  //
511  // However, anything that has birthdates or dates
512  // from the past (sys_file_metadata.content_creation_date) was saved
513  // as a SIGNED INT. It allowed birthdays of people older than 1970,
514  // but with the downside that it ends in 2038.
515  //
516  // This is now changed to utilize BIGINT everywhere, even when smaller
517  // date ranges are requested. To reduce complexity, we specifically
518  // do not evaluate "range.upper/lower" fields and use a unified type here.
519  ‪$tables[$tableName]->addColumn(
520  $this->‪quote($fieldName),
521  Types::BIGINT,
522  [
523  'default' => 0,
524  'notnull' => !($fieldConfig['config']['nullable'] ?? false),
525  'unsigned' => false,
526  ]
527  );
528  }
529  }
530 
531  // Add slug fields for all tables, defining slug columns (TCA type=slug)
532  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
533  if ((string)($fieldConfig['config']['type'] ?? '') !== 'slug'
534  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
535  ) {
536  continue;
537  }
538  ‪$tables[$tableName]->addColumn(
539  $this->‪quote($fieldName),
540  Types::STRING,
541  [
542  'length' => 2048,
543  'notnull' => false,
544  ]
545  );
546  }
547 
548  // Add json fields for all tables, defining json columns (TCA type=json)
549  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
550  if ((string)($fieldConfig['config']['type'] ?? '') !== 'json'
551  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
552  ) {
553  continue;
554  }
555  ‪$tables[$tableName]->addColumn(
556  $this->‪quote($fieldName),
557  Types::JSON,
558  [
559  'notnull' => false,
560  ]
561  );
562  }
563 
564  // Add uuid fields for all tables, defining uuid columns (TCA type=uuid)
565  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
566  if ((string)($fieldConfig['config']['type'] ?? '') !== 'uuid'
567  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
568  ) {
569  continue;
570  }
571  ‪$tables[$tableName]->addColumn(
572  $this->‪quote($fieldName),
573  Types::STRING,
574  [
575  'length' => 36,
576  'default' => '',
577  'notnull' => true,
578  ]
579  );
580  }
581 
582  // Add file fields for all tables, defining file columns (TCA type=file)
583  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
584  if ((string)($fieldConfig['config']['type'] ?? '') !== 'file'
585  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
586  ) {
587  continue;
588  }
589  ‪$tables[$tableName]->addColumn(
590  $this->‪quote($fieldName),
591  Types::INTEGER,
592  [
593  'default' => 0,
594  'notnull' => true,
595  'unsigned' => true,
596  ]
597  );
598  }
599 
600  // Add folder fields for all tables, defining file columns (TCA type=folder)
601  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
602  if ((string)($fieldConfig['config']['type'] ?? '') !== 'folder'
603  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
604  ) {
605  continue;
606  }
607  ‪$tables[$tableName]->addColumn(
608  $this->‪quote($fieldName),
609  Types::TEXT,
610  [
611  'notnull' => false,
612  ]
613  );
614  }
615 
616  // Add email fields for all tables, defining email columns (TCA type=email)
617  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
618  if ((string)($fieldConfig['config']['type'] ?? '') !== 'email'
619  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
620  ) {
621  continue;
622  }
623  $isNullable = (bool)($fieldConfig['config']['nullable'] ?? false);
624  ‪$tables[$tableName]->addColumn(
625  $this->‪quote($fieldName),
626  Types::STRING,
627  [
628  'length' => 255,
629  'default' => ($isNullable ? null : ''),
630  'notnull' => !$isNullable,
631  ]
632  );
633  }
634 
635  // Add check fields for all tables, defining check columns (TCA type=check)
636  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
637  if ((string)($fieldConfig['config']['type'] ?? '') !== 'check'
638  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
639  ) {
640  continue;
641  }
642  ‪$tables[$tableName]->addColumn(
643  $this->‪quote($fieldName),
644  Types::SMALLINT,
645  [
646  'default' => 0,
647  'notnull' => true,
648  'unsigned' => true,
649  ]
650  );
651  }
652 
653  // Add file fields for all tables, defining crop columns (TCA type=imageManipulation)
654  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
655  if ((string)($fieldConfig['config']['type'] ?? '') !== 'imageManipulation'
656  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
657  ) {
658  continue;
659  }
660  ‪$tables[$tableName]->addColumn(
661  $this->‪quote($fieldName),
662  Types::TEXT,
663  [
664  'notnull' => false,
665  ]
666  );
667  }
668 
669  // Add fields for all tables, defining language columns (TCA type=language)
670  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
671  if ((string)($fieldConfig['config']['type'] ?? '') !== 'language'
672  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
673  ) {
674  continue;
675  }
676  ‪$tables[$tableName]->addColumn(
677  $this->‪quote($fieldName),
678  Types::INTEGER,
679  [
680  'default' => 0,
681  'notnull' => true,
682  'unsigned' => false,
683  ]
684  );
685  }
686 
687  // Add fields for all tables, defining group columns (TCA type=group)
688  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
689  if ((string)($fieldConfig['config']['type'] ?? '') !== 'group'
690  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
691  ) {
692  continue;
693  }
694  if (isset($fieldConfig['config']['MM'])) {
695  ‪$tables[$tableName]->addColumn(
696  $this->‪quote($fieldName),
697  Types::INTEGER,
698  [
699  'default' => 0,
700  'notnull' => true,
701  'unsigned' => true,
702  ]
703  );
704  } else {
705  ‪$tables[$tableName]->addColumn(
706  $this->‪quote($fieldName),
707  Types::TEXT,
708  [
709  'notnull' => false,
710  ]
711  );
712  }
713  }
714 
715  // Add fields for all tables, defining flex columns (TCA type=flex)
716  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
717  if ((string)($fieldConfig['config']['type'] ?? '') !== 'flex'
718  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
719  ) {
720  continue;
721  }
722  ‪$tables[$tableName]->addColumn(
723  $this->‪quote($fieldName),
724  Types::TEXT,
725  [
726  'notnull' => false,
727  ]
728  );
729  }
730 
731  // Add fields for all tables, defining text columns (TCA type=text)
732  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
733  if ((string)($fieldConfig['config']['type'] ?? '') !== 'text'
734  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
735  ) {
736  continue;
737  }
738  ‪$tables[$tableName]->addColumn(
739  $this->‪quote($fieldName),
740  Types::TEXT,
741  [
742  'notnull' => false,
743  ]
744  );
745  }
746 
747  // Add fields for all tables, defining password columns (TCA type=password)
748  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
749  if ((string)($fieldConfig['config']['type'] ?? '') !== 'password'
750  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
751  ) {
752  continue;
753  }
754  if ($fieldConfig['config']['nullable'] ?? false) {
755  ‪$tables[$tableName]->addColumn(
756  $this->‪quote($fieldName),
757  Types::STRING,
758  [
759  'default' => null,
760  'notnull' => false,
761  ]
762  );
763  } else {
764  ‪$tables[$tableName]->addColumn(
765  $this->‪quote($fieldName),
766  Types::STRING,
767  [
768  'default' => '',
769  'notnull' => true,
770  ]
771  );
772  }
773  }
774 
775  // Add fields for all tables, defining color columns (TCA type=color)
776  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
777  if ((string)($fieldConfig['config']['type'] ?? '') !== 'color'
778  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
779  ) {
780  continue;
781  }
782  if ($fieldConfig['config']['nullable'] ?? false) {
783  ‪$tables[$tableName]->addColumn(
784  $this->‪quote($fieldName),
785  Types::STRING,
786  [
787  'length' => 7,
788  'default' => null,
789  'notnull' => false,
790  ]
791  );
792  } else {
793  ‪$tables[$tableName]->addColumn(
794  $this->‪quote($fieldName),
795  Types::STRING,
796  [
797  'length' => 7,
798  'default' => '',
799  'notnull' => true,
800  ]
801  );
802  }
803  }
804 
805  // Add fields for all tables, defining radio columns (TCA type=radio)
806  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
807  if ((string)($fieldConfig['config']['type'] ?? '') !== 'radio'
808  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
809  ) {
810  continue;
811  }
812  $hasItemsProcFunc = ($fieldConfig['config']['itemsProcFunc'] ?? '') !== '';
813  $items = $fieldConfig['config']['items'] ?? [];
814 
815  // With itemsProcFunc we can't be sure, which values are persisted. Use type string.
816  if ($hasItemsProcFunc) {
817  ‪$tables[$tableName]->addColumn(
818  $this->‪quote($fieldName),
819  Types::STRING,
820  [
821  'length' => 255,
822  'default' => '',
823  'notnull' => true,
824  ]
825  );
826  continue;
827  }
828 
829  // If no items are configured, use type string to be safe for values added directly.
830  if ($items === []) {
831  ‪$tables[$tableName]->addColumn(
832  $this->‪quote($fieldName),
833  Types::STRING,
834  [
835  'length' => 255,
836  'default' => '',
837  'notnull' => true,
838  ]
839  );
840  continue;
841  }
842 
843  // If only one value is NOT an integer use type string.
844  foreach ($items as $item) {
845  if (!‪MathUtility::canBeInterpretedAsInteger($item['value'])) {
846  ‪$tables[$tableName]->addColumn(
847  $this->‪quote($fieldName),
848  Types::STRING,
849  [
850  'length' => 255,
851  'default' => '',
852  'notnull' => true,
853  ]
854  );
855  // continue with next $tableDefinition['columns']
856  // see: DefaultTcaSchemaTest->enrichAddsRadioStringVerifyThatCorrectLoopIsContinued()
857  continue 2;
858  }
859  }
860 
861  // Use integer type.
862  $allValues = array_map(fn(array $item): int => (int)$item['value'], $items);
863  $minValue = min($allValues);
864  $maxValue = max($allValues);
865  // Try to safe some bytes - can be reconsidered to simply use Types::INTEGER.
866  $integerType = ($minValue >= -32768 && $maxValue < 32768)
867  ? Types::SMALLINT
868  : Types::INTEGER;
869  ‪$tables[$tableName]->addColumn(
870  $this->‪quote($fieldName),
871  $integerType,
872  [
873  'default' => 0,
874  'notnull' => true,
875  ]
876  );
877 
878  // Keep the house clean.
879  unset($items, $allValues, $minValue, $maxValue, $integerType);
880  }
881 
882  // Add fields for all tables, defining link columns (TCA type=link)
883  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
884  if ((string)($fieldConfig['config']['type'] ?? '') !== 'link'
885  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
886  ) {
887  continue;
888  }
889  $nullable = $fieldConfig['config']['nullable'] ?? false;
890  ‪$tables[$tableName]->addColumn(
891  $this->‪quote($fieldName),
892  Types::STRING,
893  [
894  'length' => 2048,
895  'default' => $nullable ? null : '',
896  'notnull' => !$nullable,
897  ]
898  );
899  }
900 
901  // Add fields for all tables, defining inline columns (TCA type=inline)
902  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
903  if ((string)($fieldConfig['config']['type'] ?? '') !== 'inline'
904  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
905  ) {
906  continue;
907  }
908  if (($fieldConfig['config']['MM'] ?? '') !== '' || ($fieldConfig['config']['foreign_field'] ?? '') !== '') {
909  // Parent "count" field
910  ‪$tables[$tableName]->addColumn(
911  $this->‪quote($fieldName),
912  Types::INTEGER,
913  [
914  'default' => 0,
915  'notnull' => true,
916  'unsigned' => true,
917  ]
918  );
919  } else {
920  // Inline "csv"
921  ‪$tables[$tableName]->addColumn(
922  $this->‪quote($fieldName),
923  Types::STRING,
924  [
925  'default' => '',
926  'notnull' => true,
927  'length' => 255,
928  ]
929  );
930  }
931  if (($fieldConfig['config']['foreign_field'] ?? '') !== '') {
932  // Add definition for "foreign_field" (contains parent uid) in the child table if it is not defined
933  // in child TCA or if it is "just" a "passthrough" field, and not manually configured in ext_tables.sql
934  $childTable = $fieldConfig['config']['foreign_table'];
935  if (!((‪$tables[$childTable] ?? null) instanceof Table)) {
936  throw new DefaultTcaSchemaTablePositionException('Table ' . $childTable . ' not found in schema list', 1527854474);
937  }
938  $childTableForeignFieldName = $fieldConfig['config']['foreign_field'];
939  $childTableForeignFieldConfig = ‪$GLOBALS['TCA'][$childTable]['columns'][$childTableForeignFieldName] ?? [];
940  if (($childTableForeignFieldConfig === [] || ($childTableForeignFieldConfig['config']['type'] ?? '') === 'passthrough')
941  && !$this->‪isColumnDefinedForTable($tables, $childTable, $childTableForeignFieldName)
942  ) {
943  ‪$tables[$childTable]->addColumn(
944  $this->‪quote($childTableForeignFieldName),
945  Types::INTEGER,
946  [
947  'default' => 0,
948  'notnull' => true,
949  'unsigned' => true,
950  ]
951  );
952  }
953  // Add definition for "foreign_table_field" (contains name of parent table) in the child table if it is not
954  // defined in child TCA or if it is "just" a "passthrough" field, and not manually configured in ext_tables.sql
955  $childTableForeignTableFieldName = $fieldConfig['config']['foreign_table_field'] ?? '';
956  $childTableForeignTableFieldConfig = ‪$GLOBALS['TCA'][$childTable]['columns'][$childTableForeignTableFieldName] ?? [];
957  if ($childTableForeignTableFieldName !== ''
958  && ($childTableForeignTableFieldConfig === [] || ($childTableForeignTableFieldConfig['config']['type'] ?? '') === 'passthrough')
959  && !$this->‪isColumnDefinedForTable($tables, $childTable, $childTableForeignTableFieldName)
960  ) {
961  ‪$tables[$childTable]->addColumn(
962  $this->‪quote($childTableForeignTableFieldName),
963  Types::STRING,
964  [
965  'default' => '',
966  'notnull' => true,
967  'length' => 255,
968  ]
969  );
970  }
971  }
972  }
973 
974  // Add fields for all tables, defining number columns (TCA type=number)
975  ‪$tableConnectionPlatform = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName)->getDatabasePlatform();
976  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
977  if ((string)($fieldConfig['config']['type'] ?? '') !== 'number'
978  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
979  ) {
980  continue;
981  }
982  $type = ($fieldConfig['config']['format'] ?? '') === 'decimal' ? Types::DECIMAL : Types::INTEGER;
983  $nullable = $fieldConfig['config']['nullable'] ?? false;
984  $lowerRange = $fieldConfig['config']['range']['lower'] ?? -1;
985  // Integer type for all database platforms.
986  if ($type === Types::INTEGER) {
987  ‪$tables[$tableName]->addColumn(
988  $this->‪quote($fieldName),
989  Types::INTEGER,
990  [
991  'default' => $nullable === true ? null : 0,
992  'notnull' => !$nullable,
993  'unsigned' => $lowerRange >= 0,
994  ]
995  );
996  continue;
997  }
998  // SQLite internally defines NUMERIC() fields as real, and therefore as floating numbers. pdo_sqlite
999  // then returns PHP float which can lead to rounding issues. See https://bugs.php.net/bug.php?id=81397
1000  // for more details. We create a 'string' field on SQLite as workaround.
1001  // @todo Database schema should be created with MySQL in mind and not mixed. Transforming to the
1002  // concrete database platform is handled in the database compare area. Sadly, this is not
1003  // possible right now but upcoming preparation towards doctrine/dbal 4 makes it possible to
1004  // move this "hack" to a different place.
1005  if (‪$tableConnectionPlatform instanceof DoctrineSQLitePlatform) {
1006  ‪$tables[$tableName]->addColumn(
1007  $this->‪quote($fieldName),
1008  Types::STRING,
1009  [
1010  'default' => $nullable === true ? null : '0.00',
1011  'notnull' => !$nullable,
1012  'length' => 255,
1013  ]
1014  );
1015  continue;
1016  }
1017  // Decimal for all supported platforms except SQLite
1018  ‪$tables[$tableName]->addColumn(
1019  $this->‪quote($fieldName),
1020  Types::DECIMAL,
1021  [
1022  'default' => $nullable === true ? null : 0.00,
1023  'notnull' => !$nullable,
1024  'unsigned' => $lowerRange >= 0,
1025  'precision' => 10,
1026  'scale' => 2,
1027  ]
1028  );
1029  }
1030  // Cleanup
1031  unset(‪$tableConnectionPlatform, $type, $nullable, $lowerRange);
1032 
1033  // Add fields for all tables, defining select columns (TCA type=select)
1034  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1035  if ((string)($fieldConfig['config']['type'] ?? '') !== 'select'
1036  || $this->‪isColumnDefinedForTable($tables, $tableName, $fieldName)
1037  ) {
1038  continue;
1039  }
1040  if (($fieldConfig['config']['MM'] ?? '') !== '') {
1041  // MM relation, this is a "parent count" field. Have an int.
1042  ‪$tables[$tableName]->addColumn(
1043  $this->‪quote($fieldName),
1044  Types::INTEGER,
1045  [
1046  'notnull' => true,
1047  'default' => 0,
1048  'unsigned' => true,
1049  ]
1050  );
1051  continue;
1052  }
1053 
1054  $dbFieldLength = (int)($fieldConfig['config']['dbFieldLength'] ?? 0);
1055 
1056  // If itemsProcFunc is not set, check the item values
1057  if (($fieldConfig['config']['itemsProcFunc'] ?? '') === '') {
1058  $items = $fieldConfig['config']['items'] ?? [];
1059  $itemsContainsOnlyIntegers = true;
1060  foreach ($items as $item) {
1061  if (!‪MathUtility::canBeInterpretedAsInteger($item['value'])) {
1062  $itemsContainsOnlyIntegers = false;
1063  break;
1064  }
1065  }
1066  $itemsAreAllPositive = true;
1067  foreach ($items as $item) {
1068  if ($item['value'] < 0) {
1069  $itemsAreAllPositive = false;
1070  break;
1071  }
1072  }
1073  // @todo: The dependency to renderType is unfortunate here. It's only purpose is to potentially have int fields
1074  // instead of string when this is a 'single' relation / value. However, renderType should usually not
1075  // influence DB layer at all. Maybe 'selectSingle' should be changed to an own 'type' instead to make
1076  // this more explicit. Maybe DataHandler could benefit from this as well?
1077  if (($fieldConfig['config']['renderType'] ?? '') === 'selectSingle' || ($fieldConfig['config']['maxitems'] ?? 0) === 1) {
1078  // With 'selectSingle' or with 'maxitems = 1', only a single value can be selected.
1079  if (
1080  !is_array($fieldConfig['config']['fileFolderConfig'] ?? false)
1081  && ($items !== [] || ($fieldConfig['config']['foreign_table'] ?? '') !== '')
1082  && $itemsContainsOnlyIntegers === true
1083  ) {
1084  // If the item list is empty, or if it contains only int values, an int field is enough.
1085  // Also, the config must not be a 'fileFolderConfig' field which takes string values.
1086  ‪$tables[$tableName]->addColumn(
1087  $this->‪quote($fieldName),
1088  Types::INTEGER,
1089  [
1090  'notnull' => true,
1091  'default' => 0,
1092  'unsigned' => $itemsAreAllPositive,
1093  ]
1094  );
1095  continue;
1096  }
1097  // If int is no option, have a string field.
1098  ‪$tables[$tableName]->addColumn(
1099  $this->‪quote($fieldName),
1100  Types::STRING,
1101  [
1102  'notnull' => true,
1103  'default' => '',
1104  'length' => $dbFieldLength > 0 ? $dbFieldLength : 255,
1105  ]
1106  );
1107  continue;
1108  }
1109  if ($itemsContainsOnlyIntegers) {
1110  // Multiple values can be selected and will be stored comma separated. When manual item values are
1111  // all integers, or if there is a foreign_table, we end up with a comma separated list of integers.
1112  // Using string / varchar 255 here should be long enough to store plenty of values, and can be
1113  // changed by setting 'dbFieldLength'.
1114  ‪$tables[$tableName]->addColumn(
1115  $this->‪quote($fieldName),
1116  Types::STRING,
1117  [
1118  // @todo: nullable = true is not a good default here. This stems from the fact that this
1119  // if triggers a lot of TEXT->VARCHAR() field changes during upgrade, where TEXT
1120  // is always nullable, but varchar() is not. As such, we for now declare this
1121  // nullable, but could have a look at it later again when a value upgrade
1122  // for such cases is in place that updates existing null fields to empty string.
1123  'notnull' => false,
1124  'default' => '',
1125  'length' => $dbFieldLength > 0 ? $dbFieldLength : 255,
1126  ]
1127  );
1128  continue;
1129  }
1130  }
1131 
1132  if ($dbFieldLength > 0) {
1133  // If nothing else matches, but there is a dbFieldLength set, have varchar with that length.
1134  ‪$tables[$tableName]->addColumn(
1135  $this->‪quote($fieldName),
1136  Types::STRING,
1137  [
1138  'notnull' => true,
1139  'default' => '',
1140  'length' => $dbFieldLength,
1141  ]
1142  );
1143  } else {
1144  // Final fallback creates a (nullable) text field.
1145  ‪$tables[$tableName]->addColumn(
1146  $this->‪quote($fieldName),
1147  Types::TEXT,
1148  [
1149  'notnull' => false,
1150  ]
1151  );
1152  }
1153  }
1154 
1155  }
1156 
1157  return ‪$tables;
1158  }
1159 
1169  protected function enrichMmTables(‪$tables): array
1170  {
1171  foreach (‪$GLOBALS['TCA'] as $tableName => $tableDefinition) {
1172  if (!is_array($tableDefinition['columns'] ?? false)) {
1173  // TCA definition in general is broken if there are no specified columns. Skip to be sure here.
1174  continue;
1175  }
1176  foreach ($tableDefinition['columns'] as $tcaColumn) {
1177  if (
1178  !is_array($tcaColumn['config'] ?? false)
1179  || !is_string($tcaColumn['config']['type'] ?? false)
1180  || !in_array($tcaColumn['config']['type'], ['select', 'group', 'inline', 'category'], true)
1181  || !is_string($tcaColumn['config']['MM'] ?? false)
1182  // Consider this mm only if looking at it from the local side
1183  || ($tcaColumn['config']['MM_opposite_field'] ?? false)
1184  ) {
1185  // Broken TCA or not of expected type, or no MM, or foreign side
1186  continue;
1187  }
1188  $mmTableName = $tcaColumn['config']['MM'];
1189  if (!array_key_exists($mmTableName, ‪$tables)) {
1190  // If the mm table is defined, work with it. Else add at and.
1191  ‪$tables[$mmTableName] = GeneralUtility::makeInstance(
1192  Table::class,
1193  $mmTableName
1194  );
1195  }
1196 
1197  // Add 'uid' field with primary key if multiple is set: 'multiple' allows using a left or right
1198  // side more than once in a relation which would lead to duplicate primary key entries. To
1199  // avoid this, we add a uid column and make it primary key instead.
1200  $needsUid = (bool)($tcaColumn['config']['multiple'] ?? false);
1201  if ($needsUid && !$this->‪isColumnDefinedForTable($tables, $mmTableName, 'uid')) {
1202  ‪$tables[$mmTableName]->addColumn(
1203  $this->‪quote('uid'),
1204  Types::INTEGER,
1205  [
1206  'notnull' => true,
1207  'unsigned' => true,
1208  'autoincrement' => true,
1209  ]
1210  );
1211  ‪$tables[$mmTableName]->setPrimaryKey(['uid']);
1212  }
1213 
1214  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'uid_local')) {
1215  ‪$tables[$mmTableName]->addColumn(
1216  $this->‪quote('uid_local'),
1217  Types::INTEGER,
1218  [
1219  'default' => 0,
1220  'notnull' => true,
1221  'unsigned' => true,
1222  ]
1223  );
1224  }
1225  if (!$this->‪isIndexDefinedForTable($tables, $mmTableName, 'uid_local')) {
1226  ‪$tables[$mmTableName]->addIndex(['uid_local'], 'uid_local');
1227  }
1228 
1229  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'uid_foreign')) {
1230  ‪$tables[$mmTableName]->addColumn(
1231  $this->‪quote('uid_foreign'),
1232  Types::INTEGER,
1233  [
1234  'default' => 0,
1235  'notnull' => true,
1236  'unsigned' => true,
1237  ]
1238  );
1239  }
1240  if (!$this->‪isIndexDefinedForTable($tables, $mmTableName, 'uid_foreign')) {
1241  ‪$tables[$mmTableName]->addIndex(['uid_foreign'], 'uid_foreign');
1242  }
1243 
1244  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'sorting')) {
1245  ‪$tables[$mmTableName]->addColumn(
1246  $this->‪quote('sorting'),
1247  Types::INTEGER,
1248  [
1249  'default' => 0,
1250  'notnull' => true,
1251  'unsigned' => true,
1252  ]
1253  );
1254  }
1255  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'sorting_foreign')) {
1256  ‪$tables[$mmTableName]->addColumn(
1257  $this->‪quote('sorting_foreign'),
1258  Types::INTEGER,
1259  [
1260  'default' => 0,
1261  'notnull' => true,
1262  'unsigned' => true,
1263  ]
1264  );
1265  }
1266 
1267  if (!empty($tcaColumn['config']['MM_oppositeUsage'])) {
1268  // This local table can be the target of multiple foreign tables and table fields. The mm table
1269  // thus needs two further fields to specify which foreign/table field combination links is used.
1270  // Those are stored in two additional fields called "tablenames" and "fieldname".
1271  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'tablenames')) {
1272  ‪$tables[$mmTableName]->addColumn(
1273  $this->‪quote('tablenames'),
1274  Types::STRING,
1275  [
1276  'default' => '',
1277  'length' => 64,
1278  'notnull' => true,
1279  ]
1280  );
1281  }
1282  if (!$this->‪isColumnDefinedForTable($tables, $mmTableName, 'fieldname')) {
1283  ‪$tables[$mmTableName]->addColumn(
1284  $this->‪quote('fieldname'),
1285  Types::STRING,
1286  [
1287  'default' => '',
1288  'length' => 64,
1289  'notnull' => true,
1290  ]
1291  );
1292  }
1293  }
1294 
1295  // Primary key handling: If there is a uid field, PK has been added above already.
1296  // Otherwise, the PK combination is either "uid_local, uid_foreign", or
1297  // "uid_local, uid_foreign, tablenames, fieldname" if this is a multi-foreign setup.
1298  if (!$needsUid && ‪$tables[$mmTableName]->getPrimaryKey() === null && !empty($tcaColumn['config']['MM_oppositeUsage'])) {
1299  ‪$tables[$mmTableName]->setPrimaryKey(['uid_local', 'uid_foreign', 'tablenames', 'fieldname']);
1300  } elseif (!$needsUid && ‪$tables[$mmTableName]->getPrimaryKey() === null) {
1301  ‪$tables[$mmTableName]->setPrimaryKey(['uid_local', 'uid_foreign']);
1302  }
1303  }
1304  }
1305  return ‪$tables;
1306  }
1307 
1314  protected function ‪isColumnDefinedForTable(array ‪$tables, string $tableName, string $fieldName): bool
1315  {
1316  return (‪$tables[$tableName] ?? null)?->hasColumn($fieldName) ?? false;
1317  }
1318 
1325  protected function ‪isIndexDefinedForTable(array ‪$tables, string $tableName, string $indexName): bool
1326  {
1327  return (‪$tables[$tableName] ?? null)?->hasIndex($indexName) ?? false;
1328  }
1329 
1330  protected function ‪quote(string ‪$identifier): string
1331  {
1332  return '`' . ‪$identifier . '`';
1333  }
1334 }
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\isIndexDefinedForTable
‪isIndexDefinedForTable(array $tables, string $tableName, string $indexName)
Definition: DefaultTcaSchema.php:1325
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\quote
‪quote(string $identifier)
Definition: DefaultTcaSchema.php:1330
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\isColumnDefinedForTable
‪isColumnDefinedForTable(array $tables, string $tableName, string $fieldName)
Definition: DefaultTcaSchema.php:1314
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$pidColumnAdded
‪$pidColumnAdded
Definition: DefaultTcaSchema.php:98
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$tables
‪foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) return $tables
Definition: DefaultTcaSchema.php:1034
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$tables
‪$tables
Definition: DefaultTcaSchema.php:70
‪TYPO3\CMS\Core\Database\Schema
Definition: ColumnDiff.php:18
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema
Definition: DefaultTcaSchema.php:43
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$tables
‪if(! $this->isColumnDefinedForTable($tables, $mmTableName, 'uid_local')) if(! $this->isIndexDefinedForTable($tables, $mmTableName, 'uid_local')) if(! $this->isColumnDefinedForTable($tables, $mmTableName, 'uid_foreign')) if(! $this->isIndexDefinedForTable($tables, $mmTableName, 'uid_foreign')) if(! $this->isColumnDefinedForTable($tables, $mmTableName, 'sorting')) if(! $this->isColumnDefinedForTable($tables, $mmTableName, 'sorting_foreign')) if(!empty($tcaColumn['config']['MM_oppositeUsage'])) if(! $needsUid && $tables[$mmTableName]->getPrimaryKey()===null &&!empty($tcaColumn['config']['MM_oppositeUsage'])) elseif(! $needsUid && $tables[$mmTableName]->getPrimaryKey()===null) return $tables
Definition: DefaultTcaSchema.php:1300
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Core\Database\Query\QueryHelper\getDateTimeTypes
‪static array getDateTimeTypes()
Definition: QueryHelper.php:211
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$tables
‪if(! $this->isColumnDefinedForTable($tables, $tableName, 'pid')) if(!empty($tableDefinition['ctrl']['tstamp']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['tstamp'])) if(!empty($tableDefinition['ctrl']['crdate']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['crdate'])) if(!empty($tableDefinition['ctrl']['delete']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['delete'])) if(!empty($tableDefinition['ctrl']['enablecolumns']['disabled']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['disabled'])) if(!empty($tableDefinition['ctrl']['enablecolumns']['starttime']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['starttime'])) if(!empty($tableDefinition['ctrl']['enablecolumns']['endtime']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['endtime'])) if(!empty($tableDefinition['ctrl']['enablecolumns']['fe_group']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['enablecolumns']['fe_group'])) if(!empty($tableDefinition['ctrl']['sortby']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['sortby'])) if($pidColumnAdded &&! $this->isIndexDefinedForTable($tables, $tableName, 'parent')) if(!empty($tableDefinition['ctrl']['descriptionColumn']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['descriptionColumn'])) if(!empty($tableDefinition['ctrl']['editlock']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['editlock'])) if(!empty($tableDefinition['ctrl']['languageField']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['languageField'])) if(!empty($tableDefinition['ctrl']['languageField']) &&!empty($tableDefinition['ctrl']['transOrigPointerField']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['transOrigPointerField'])) if(!empty($tableDefinition['ctrl']['languageField']) &&!empty($tableDefinition['ctrl']['translationSource']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['translationSource'])) if(!empty($tableDefinition['ctrl']['languageField']) &&!empty($tableDefinition['ctrl']['transOrigPointerField']) &&! $this->isColumnDefinedForTable($tables, $tableName, 'l10n_state')) if(!empty($tableDefinition['ctrl']['origUid']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['origUid'])) if(!empty($tableDefinition['ctrl']['transOrigDiffSourceField']) &&! $this->isColumnDefinedForTable($tables, $tableName, $tableDefinition['ctrl']['transOrigDiffSourceField'])) if(!empty($tableDefinition['ctrl']['versioningWS']) &&(bool) $tableDefinition['ctrl']['versioningWS']===true &&! $this->isColumnDefinedForTable($tables, $tableName, 't3ver_oid')) if(!empty($tableDefinition['ctrl']['versioningWS']) &&(bool) $tableDefinition['ctrl']['versioningWS']===true &&! $this->isColumnDefinedForTable($tables, $tableName, 't3ver_wsid')) if(!empty($tableDefinition['ctrl']['versioningWS']) &&(bool) $tableDefinition['ctrl']['versioningWS']===true &&! $this->isColumnDefinedForTable($tables, $tableName, 't3ver_state')) if(!empty($tableDefinition['ctrl']['versioningWS']) &&(bool) $tableDefinition['ctrl']['versioningWS']===true &&! $this->isColumnDefinedForTable($tables, $tableName, 't3ver_stage')) if(!empty($tableDefinition['ctrl']['versioningWS']) &&(bool) $tableDefinition['ctrl']['versioningWS']===true &&! $this->isIndexDefinedForTable($tables, $tableName, 't3ver_oid')) return $tables
Definition: DefaultTcaSchema.php:431
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\$tableConnectionPlatform
‪foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) foreach($tableDefinition['columns'] as $fieldName=> $fieldConfig) $tableConnectionPlatform
Definition: DefaultTcaSchema.php:975
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Database\Schema\DefaultTcaSchema\enrichSingleTableFieldsFromTcaCtrl
‪array< non-empty-string, enrich(array $tables):array { $tableNamesFromTca=array_keys( $GLOBALS[ 'TCA']);$existingTableNames=array_keys( $tables);foreach( $tableNamesFromTca as $tableName) { if(!in_array( $tableName, $existingTableNames, true)) { throw new \RuntimeException('Table name ' . $tableName . ' does not exist in incoming table list', 1696424993);} } $tables=$this-> enrichSingleTableFieldsFromTcaCtrl($tables)
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Database\Schema\Exception\DefaultTcaSchemaTablePositionException
Definition: DefaultTcaSchemaTablePositionException.php:27