[BUGFIX] Fix foreign_table_where GROUP BY handling
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / DatabaseRecordTypeValue.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\FormDataProvider;
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
17 use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20
21 /**
22 * Determine the final TCA type value
23 */
24 class DatabaseRecordTypeValue implements FormDataProviderInterface
25 {
26 /**
27 * TCA type value depends on several parameters. The simple case is
28 * a direct lookup in the database row, which then just needs handling
29 * in case the row is a localization overlay.
30 * More complex is the field:field syntax that can look up the actual
31 * value in a different table.
32 *
33 * @param array $result
34 * @return array
35 * @throws \UnexpectedValueException
36 */
37 public function addData(array $result)
38 {
39 if (!isset($result['processedTca']['types'])
40 || !is_array($result['processedTca']['types'])
41 || empty($result['processedTca']['types'])
42 ) {
43 throw new \UnexpectedValueException(
44 'At least one "types" array must be defined for table ' . $result['tableName'] . ', preferred "0"',
45 1438185331
46 );
47 }
48
49 // Guard clause to suppress any calculation if record type value has been set from outside already
50 if ($result['recordTypeValue'] !== '') {
51 return $result;
52 }
53
54 $recordTypeValue = '0';
55 if (!empty($result['processedTca']['ctrl']['type'])) {
56 $tcaTypeField = $result['processedTca']['ctrl']['type'];
57
58 if (strpos($tcaTypeField, ':') === false) {
59 // $tcaTypeField is the name of a field in database row
60 if (!array_key_exists($tcaTypeField, $result['databaseRow'])) {
61 throw new \UnexpectedValueException(
62 'TCA table ' . $result['tableName'] . ' ctrl[\'type\'] is set to ' . $tcaTypeField . ', but'
63 . ' this field does not exist in the database of this table',
64 1438183881
65 );
66 }
67 $recordTypeValue = $result['databaseRow'][$tcaTypeField];
68 } else {
69 // If type is configured as localField:foreignField, fetch the type value from
70 // a foreign table. localField then point to a group or select field in the own table,
71 // this points to a record in a foreign table and the value of foreignField is then
72 // used as type field. This was introduced for some FAL scenarios.
73 list($pointerField, $foreignTableTypeField) = explode(':', $tcaTypeField);
74
75 $relationType = $result['processedTca']['columns'][$pointerField]['config']['type'];
76 if ($relationType !== 'select' && $relationType !== 'group') {
77 throw new \UnexpectedValueException(
78 'TCA foreign field pointer fields are only allowed to be used with group or select field types.'
79 . ' Handling field ' . $pointerField . ' with type configured as ' . $tcaTypeField,
80 1325862241
81 );
82 }
83
84 $foreignUid = $result['databaseRow'][$pointerField];
85 // Resolve the foreign record only if there is a uid, otherwise fall back 0
86 if (!empty($foreignUid)) {
87 // Determine table name to fetch record from
88 if ($relationType === 'select') {
89 $foreignTable = $result['processedTca']['columns'][$pointerField]['config']['foreign_table'];
90 } else {
91 $allowedTables = explode(',', $result['processedTca']['columns'][$pointerField]['config']['allowed']);
92 // Always take the first configured table.
93 $foreignTable = $allowedTables[0];
94 }
95 if (empty($foreignTable)) {
96 throw new \UnexpectedValueException(
97 'No target table defined for type config field ' . $pointerField . ' of table ' . $result['tableName'],
98 1438253614
99 );
100 }
101 if (!MathUtility::canBeInterpretedAsInteger($foreignUid) && is_array($foreignUid[0])) {
102 // A group relation - has been resolved to array by TcaGroup data provider already
103 $foreignUid = $foreignUid[0]['uid'];
104 }
105 // Fetch field of this foreign row from db
106 $foreignRow = $this->getDatabaseRow($foreignTable, $foreignUid, $foreignTableTypeField);
107 if ($foreignRow[$foreignTableTypeField]) {
108 // @todo: It might be necessary to fetch the value from default language record as well here,
109 // @todo: this was buggy in the "old" implementation and never worked. It was therefor left out here for now.
110 // @todo: To implement that, see if the foreign row is a localized overlay, fetch default and merge exclude
111 $recordTypeValue = $foreignRow[$foreignTableTypeField];
112 }
113 }
114 }
115 }
116
117 // Throw another exception if determined value and '0' and '1' do not exist
118 if (empty($result['processedTca']['types'][$recordTypeValue])
119 && empty($result['processedTca']['types']['0'])
120 && empty($result['processedTca']['types']['1'])
121 ) {
122 throw new \UnexpectedValueException(
123 'Type value ' . $recordTypeValue . ' from database record not defined in TCA of table '
124 . $result['tableName'] . ' and neither 0 nor 1 are defined as fallback.',
125 1438185437
126 );
127 }
128
129 // Check the determined value actually exists as types key, otherwise fall back to 0 or 1, 1 for "historical reasons"
130 if (empty($result['processedTca']['types'][$recordTypeValue])) {
131 $recordTypeValue = !empty($result['processedTca']['types']['0']) ? '0' : '1';
132 }
133
134 $result['recordTypeValue'] = (string)$recordTypeValue;
135 return $result;
136 }
137
138 /**
139 * Retrieve the requested row from the database
140 *
141 * @param string $tableName
142 * @param int $uid
143 * @param string $fieldName
144 * @return array
145 */
146 protected function getDatabaseRow(string $tableName, int $uid, string $fieldName): array
147 {
148 $row = BackendUtility::getRecord($tableName, $uid, $fieldName);
149
150 return $row ?: [];
151 }
152 }