• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / flux / 27757211628

18 Jun 2026 11:46AM UTC coverage: 89.162% (-3.5%) from 92.646%
27757211628

Pull #2288

github

web-flow
Merge 967f03443 into 2614049c6
Pull Request #2288: [FEATURE] Prepare for v14 support

210 of 348 new or added lines in 56 files covered. (60.34%)

121 existing lines in 9 files now uncovered.

6228 of 6985 relevant lines covered (89.16%)

40.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.75
/Classes/Provider/PageProvider.php
1
<?php
2
namespace FluidTYPO3\Flux\Provider;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Flux\Builder\ViewBuilder;
12
use FluidTYPO3\Flux\Enum\ExtensionOption;
13
use FluidTYPO3\Flux\Enum\FormOption;
14
use FluidTYPO3\Flux\Form;
15
use FluidTYPO3\Flux\Form\Transformation\FormDataTransformer;
16
use FluidTYPO3\Flux\Service\CacheService;
17
use FluidTYPO3\Flux\Service\PageService;
18
use FluidTYPO3\Flux\Service\TypoScriptService;
19
use FluidTYPO3\Flux\Service\WorkspacesAwareRecordService;
20
use FluidTYPO3\Flux\Utility\ExtensionConfigurationUtility;
21
use FluidTYPO3\Flux\Utility\ExtensionNamingUtility;
22
use FluidTYPO3\Flux\Utility\MiscellaneousUtility;
23
use FluidTYPO3\Flux\Utility\RecursiveArrayUtility;
24
use TYPO3\CMS\Core\DataHandling\DataHandler;
25
use TYPO3\CMS\Core\Utility\GeneralUtility;
26
use TYPO3\CMS\Core\Utility\RootlineUtility;
27
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
28

29
/**
30
 * Page Configuration Provider
31
 *
32
 * Main Provider - triggers only on
33
 * records which have a selected action.
34
 * All other page records will be associated
35
 * with the SubPageProvider instead.
36
 */
37
class PageProvider extends AbstractProvider implements ProviderInterface
38
{
39
    public const string FIELD_NAME_MAIN = 'tx_fed_page_flexform';
40
    public const string FIELD_NAME_SUB = 'tx_fed_page_flexform_sub';
41
    public const string FIELD_ACTION_MAIN = 'tx_fed_page_controller_action';
42
    public const string FIELD_ACTION_SUB = 'tx_fed_page_controller_action_sub';
43

44
    protected ?string $tableName = 'pages';
45
    protected ?string $parentFieldName = 'pid';
46
    protected ?string $fieldName = self::FIELD_NAME_MAIN;
47
    protected string $extensionKey = 'FluidTYPO3.Flux';
48
    protected ?string $controllerName = 'Page';
49
    protected ?string $configurationSectionName = 'Configuration';
50
    protected ?string $pluginName = 'Page';
51

52
    private static array $cache = [];
53

54
    protected PageService $pageService;
55

56
    public function __construct(
57
        FormDataTransformer $formDataTransformer,
58
        WorkspacesAwareRecordService $recordService,
59
        ViewBuilder $viewBuilder,
60
        CacheService $cacheService,
61
        TypoScriptService $typoScriptService,
62
        PageService $pageService
63
    ) {
64
        parent::__construct($formDataTransformer, $recordService, $viewBuilder, $cacheService, $typoScriptService);
116✔
65
        $this->pageService = $pageService;
116✔
66
    }
67

68
    /**
69
     * Returns TRUE that this Provider should trigger if:
70
     *
71
     * - table matches 'pages'
72
     * - field is NULL or matches self::FIELD_NAME
73
     * - a selection was made in the "template for this page" field
74
     */
75
    public function trigger(array $row, ?string $table, ?string $field, ?string $extensionKey = null): bool
76
    {
77
        $isRightTable = ($table === $this->tableName);
4✔
78
        $isRightField = in_array($field, [null, self::FIELD_NAME_MAIN, self::FIELD_NAME_SUB], true);
4✔
79
        return (true === $isRightTable && true === $isRightField);
4✔
80
    }
81

82
    public function getForm(array $row, ?string $forField = null): ?Form
83
    {
84
        if ($row['deleted'] ?? false) {
20✔
85
            return null;
8✔
86
        }
87

88
        // If field is main field and "this page" has no selection or field is sub field and "subpages" has no selection
89
        if (($forField === self::FIELD_NAME_MAIN && empty($row[self::FIELD_ACTION_MAIN]))
12✔
90
            || ($forField === self::FIELD_NAME_SUB && empty($row[self::FIELD_ACTION_SUB]))
12✔
91
        ) {
92
            // The page inherits page layout from parent(s). Read the root line for the first page that defines a value
93
            // in the sub-action field, then use that record and resolve the Form used in the sub-configuration field.
94
            // If the row is a new page, use the inherited form from the parent page.
95
            $pageUid = $row['uid'] ?? 0;
8✔
96
            $pageUidIsParent = false;
8✔
97
            if (is_string($pageUid) && substr($pageUid, 0, 3) === 'NEW') {
8✔
98
                $pageUid = $row['pid'] ?? 0;
4✔
99
                $pageUidIsParent = true;
4✔
100
            }
101

102
            $pageTemplateConfiguration = $this->pageService->getPageTemplateConfiguration(
8✔
103
                (int) $pageUid,
8✔
104
                $pageUidIsParent
8✔
105
            );
8✔
106

107
            $form = parent::getForm($pageTemplateConfiguration['record_sub'] ?? $row, self::FIELD_NAME_SUB);
8✔
108
        } else {
109
            $form = parent::getForm($row, $forField);
4✔
110
        }
111

112
        return $form;
12✔
113
    }
114

115
    public function getExtensionKey(array $row, ?string $forField = null): string
116
    {
117
        $controllerExtensionKey = $this->getControllerExtensionKeyFromRecord($row, $forField);
8✔
118
        if (!empty($controllerExtensionKey)) {
8✔
119
            return ExtensionNamingUtility::getExtensionKey($controllerExtensionKey);
4✔
120
        }
121
        return $this->extensionKey;
4✔
122
    }
123

124
    public function getTemplatePathAndFilename(array $row, ?string $forField = null): ?string
125
    {
126
        $templatePathAndFilename = $this->templatePathAndFilename;
4✔
127
        $action = $this->getControllerActionFromRecord($row, $forField);
4✔
128
        if (!empty($action)) {
4✔
129
            $pathsOrExtensionKey = $this->templatePaths
4✔
130
                ?? ExtensionNamingUtility::getExtensionKey($this->getControllerExtensionKeyFromRecord($row, $forField));
4✔
131
            $templatePaths = $this->viewBuilder->buildTemplatePaths($pathsOrExtensionKey);
4✔
132
            $action = ucfirst($action);
4✔
133
            $templatePathAndFilename = $templatePaths->resolveTemplateFileForControllerAndActionAndFormat(
4✔
134
                $this->getControllerNameFromRecord($row),
4✔
135
                $action
4✔
136
            );
4✔
137
        }
138
        return $templatePathAndFilename;
4✔
139
    }
140

141
    public function getControllerExtensionKeyFromRecord(array $row, ?string $forField = null): string
142
    {
143
        $action = $this->getControllerActionReferenceFromRecord($row, $forField);
8✔
144
        $offset = strpos($action, '->');
8✔
145
        if ($offset !== false) {
8✔
146
            return substr($action, 0, $offset);
4✔
147
        }
148
        return $this->extensionKey;
4✔
149
    }
150

151
    public function getControllerActionFromRecord(array $row, ?string $forField = null): string
152
    {
153
        $action = $this->getControllerActionReferenceFromRecord($row, $forField);
20✔
154
        $parts = explode('->', $action);
20✔
155
        $controllerActionName = end($parts);
20✔
156
        if (empty($controllerActionName)) {
20✔
157
            return 'default';
4✔
158
        }
159
        $controllerActionName[0] = strtolower($controllerActionName[0]);
16✔
160
        return $controllerActionName;
16✔
161
    }
162

163
    public function getControllerActionReferenceFromRecord(array $row, ?string $forField = null): string
164
    {
165
        if (($forField === self::FIELD_NAME_MAIN || $forField === null) && !empty($row[self::FIELD_ACTION_MAIN])) {
16✔
166
            return is_array($row[self::FIELD_ACTION_MAIN])
8✔
167
                ? $row[self::FIELD_ACTION_MAIN][0]
×
168
                : $row[self::FIELD_ACTION_MAIN];
8✔
169
        } elseif ($forField === self::FIELD_NAME_SUB && !empty($row[self::FIELD_ACTION_SUB])) {
8✔
170
            return is_array($row[self::FIELD_ACTION_SUB])
4✔
171
                ? $row[self::FIELD_ACTION_SUB][0]
×
172
                : $row[self::FIELD_ACTION_SUB];
4✔
173
        }
174
        if (isset($row['uid'])) {
4✔
NEW
175
            $configuration = $this->pageService->getPageTemplateConfiguration((int) $row['uid']);
×
176
            $fieldName = self::FIELD_ACTION_SUB;
×
177
            if ($forField === self::FIELD_NAME_MAIN) {
×
178
                $fieldName = self::FIELD_ACTION_MAIN;
×
179
            }
180
            return ($configuration[$fieldName] ?? 'flux->default') ?: 'flux->default';
×
181
        }
182
        return 'flux->default';
4✔
183
    }
184

185
    public function getFlexFormValues(array $row, ?string $forField = null): array
186
    {
187
        $immediateConfiguration = $this->getFlexFormValuesSingle($row);
8✔
188
        $inheritedConfiguration = $this->getInheritedConfiguration($row);
8✔
189
        return RecursiveArrayUtility::merge($inheritedConfiguration, $immediateConfiguration);
8✔
190
    }
191

192
    public function getFlexFormValuesSingle(array $row, ?string $forField = null): array
193
    {
194
        $fieldName = $forField ?? $this->getFieldName($row);
8✔
195
        $form = $this->getForm($row, $forField);
8✔
196
        $immediateConfiguration = $this->formDataTransformer->convertFlexFormContentToArray(
8✔
197
            $row[$fieldName] ?? '',
8✔
198
            $form,
8✔
199
            null,
8✔
200
            null
8✔
201
        );
8✔
202
        return $immediateConfiguration;
8✔
203
    }
204

205
    public function postProcessRecord(
206
        string $operation,
207
        int $id,
208
        array $row,
209
        DataHandler $reference,
210
        array $removals = []
211
    ): bool {
212
        if ($operation === 'update') {
4✔
213
            $stored = $this->recordService->getSingle((string) $this->getTableName($row), '*', $id);
4✔
214
            if (!$stored) {
4✔
215
                return false;
×
216
            }
217
            $before = $stored;
4✔
218
            $tableName = $this->getTableName($stored);
4✔
219

220
            $record = RecursiveArrayUtility::mergeRecursiveOverrule(
4✔
221
                $stored,
4✔
222
                $reference->datamap[$this->tableName][$id] ?? []
4✔
223
            );
4✔
224

225
            foreach ([self::FIELD_NAME_MAIN, self::FIELD_NAME_SUB] as $tableFieldName) {
4✔
226
                $form = $this->getForm($record, $tableFieldName);
4✔
227
                if (!$form) {
4✔
228
                    continue;
×
229
                }
230

231
                $removeForField = [];
4✔
232
                $protected = array_flip($this->extractFieldNamesToProtect($record, $tableFieldName));
4✔
233
                foreach ($form->getFields() as $field) {
4✔
234
                    /** @var Form\Container\Sheet $parent */
235
                    $parent = $field->getParent();
4✔
236
                    $fieldName = (string) $field->getName();
4✔
237
                    $sheetName = (string) $parent->getName();
4✔
238
                    $inherit = (bool) $field->getInherit();
4✔
239
                    $inheritEmpty = (bool) $field->getInheritEmpty();
4✔
240
                    if (is_array($record[$tableFieldName]['data'] ?? null)) {
4✔
241
                        $value = $record[$tableFieldName]['data'][$sheetName]['lDEF'][$fieldName]['vDEF'] ?? null;
4✔
242
                        $inheritedConfiguration = $this->getInheritedConfiguration($record);
4✔
243
                        if (!isset($inheritedConfiguration[$fieldName])) {
4✔
244
                            continue;
4✔
245
                        }
246
                        $inheritedValue = $this->getInheritedPropertyValueByDottedPath(
×
247
                            $inheritedConfiguration,
×
248
                            $fieldName
×
249
                        );
×
250
                        $empty = (true === empty($value) && $value !== '0' && $value !== 0);
×
251
                        $same = ($inheritedValue === $value);
×
252
                        $protected = (bool) ($protected[$fieldName] ?? false);
×
253
                        if (!$protected && ($same && $inherit || ($inheritEmpty && $empty))) {
×
254
                            $removeForField[] = $fieldName;
×
255
                        }
256
                    }
257
                }
258
                $removeForField = array_merge(
4✔
259
                    $removeForField,
4✔
260
                    $this->extractFieldNamesToClear($record, $tableFieldName)
4✔
261
                );
4✔
262
                $removeForField = array_unique($removeForField);
4✔
263

264
                if (!empty($stored[$tableFieldName])) {
4✔
265
                    $stored[$tableFieldName] = MiscellaneousUtility::cleanFlexFormXml(
4✔
266
                        $stored[$tableFieldName],
4✔
267
                        $removeForField
4✔
268
                    );
4✔
269
                }
270
            }
271

272
            if ($before !== $stored) {
4✔
273
                $this->recordService->update((string) $tableName, $stored);
4✔
274
            }
275
        }
276

277
        return false;
4✔
278
    }
279

280
    public function processTableConfiguration(array $row, array $configuration): array
281
    {
282
        $currentPageRecord = $this->recordService->getSingle(
12✔
283
            (string) $this->getTableName($row),
12✔
284
            'uid,' . self::FIELD_NAME_MAIN . ',' . self::FIELD_NAME_SUB,
12✔
285
            $configuration['vanillaUid']
12✔
286
        );
12✔
287
        if (!$currentPageRecord) {
12✔
288
            return $configuration;
4✔
289
        }
290

291
        $tree = array_reverse($this->getInheritanceTree($currentPageRecord), true);
8✔
292

293
        $inheritedConfiguration = [];
8✔
294

295
        if (!empty($currentPageRecord[self::FIELD_NAME_SUB])) {
8✔
296
            $currentPageRecord[self::FIELD_NAME_SUB] = $this->convertXmlToArray(
×
297
                $currentPageRecord[self::FIELD_NAME_SUB]
×
298
            ) ?? [];
×
299
        }
300
        /** @var Form&Form $form */
301
        $form = $this->getForm($row, self::FIELD_NAME_SUB);
8✔
302
        foreach ($tree as $branch) {
8✔
303
            if (!empty($branch[self::FIELD_NAME_SUB])) {
8✔
304
                $branchData = $this->convertXmlToArray($branch[self::FIELD_NAME_SUB] ?? '') ?? [];
8✔
305
                $inheritedConfiguration = RecursiveArrayUtility::mergeRecursiveOverrule(
8✔
306
                    $inheritedConfiguration,
8✔
307
                    $branchData
8✔
308
                );
8✔
309
            }
310
        }
311

312
        $inheritedConfigurationForFields = [];
8✔
313
        foreach ([self::FIELD_NAME_MAIN, self::FIELD_NAME_SUB] as $field) {
8✔
314
            $inheritedConfigurationForFields[$field] = $inheritedConfiguration;
8✔
315
            $dataMirror = ['data' => []];
8✔
316
            $this->extractDataStorageMirrorWithInheritableFields($form, $dataMirror['data']);
8✔
317
            $this->unsetUninheritableFieldsInInheritedConfiguration(
8✔
318
                $inheritedConfigurationForFields[$field],
8✔
319
                $currentPageRecord[self::FIELD_NAME_MAIN],
8✔
320
                $dataMirror
8✔
321
            );
8✔
322
        }
323

324
        if (!empty($configuration['databaseRow'][self::FIELD_NAME_MAIN])) {
8✔
325
            if (is_array($configuration['databaseRow'][self::FIELD_NAME_MAIN])) {
8✔
326
                $currentData = $configuration['databaseRow'][self::FIELD_NAME_MAIN];
4✔
327
            } else {
328
                $currentData = $this->convertXmlToArray($configuration['databaseRow'][self::FIELD_NAME_MAIN]) ?? [];
4✔
329
            }
330
            $configuration['databaseRow'][self::FIELD_NAME_MAIN] = RecursiveArrayUtility::mergeRecursiveOverrule(
8✔
331
                $inheritedConfigurationForFields[self::FIELD_NAME_MAIN],
8✔
332
                $currentData,
8✔
333
                false,
8✔
334
                true
8✔
335
            );
8✔
336
        }
337

338
        if (is_array($configuration['databaseRow'][self::FIELD_NAME_SUB])) {
8✔
339
            $subData = $configuration['databaseRow'][self::FIELD_NAME_SUB];
4✔
340
        } else {
341
            $subData = $this->convertXmlToArray($configuration['databaseRow'][self::FIELD_NAME_SUB]) ?? [];
4✔
342
        }
343

344
        $configuration['databaseRow'][self::FIELD_NAME_SUB] = RecursiveArrayUtility::mergeRecursiveOverrule(
8✔
345
            $inheritedConfigurationForFields[self::FIELD_NAME_SUB],
8✔
346
            $subData
8✔
347
        );
8✔
348

349
        return parent::processTableConfiguration($row, $configuration);
8✔
350
    }
351

352
    public function getPreview(array $row): array
353
    {
354
        $previewContent = $this->viewBuilder->buildPreviewView(
×
355
            $this->getControllerExtensionKeyFromRecord($row),
×
356
            $this->getControllerNameFromRecord($row),
×
357
            $this->getControllerActionFromRecord($row),
×
358
            $this->getPluginName() ?? $this->getControllerNameFromRecord($row),
×
359
            $this->getTemplatePathAndFilename($row)
×
360
        )->getPreview($this, $row, true);
×
361
        return [null, $previewContent, empty($previewContent)];
×
362
    }
363

364
    /**
365
     * @param array|string|null $immediateConfiguration
366
     */
367
    private function unsetUninheritableFieldsInInheritedConfiguration(
368
        array &$inheritedConfiguration,
369
        &$immediateConfiguration,
370
        array &$dataMirror
371
    ): void {
372
        foreach ($inheritedConfiguration as $key => &$value) {
8✔
373
            if (!is_array($value)) {
8✔
374
                continue;
8✔
375
            }
376

377
            $inheritedValueIsSection = $this->checkIsSection($inheritedConfiguration[$key] ?? []);
8✔
378
            if (!isset($dataMirror[$key]) && !$inheritedValueIsSection) {
8✔
379
                unset($inheritedConfiguration[$key]);
×
380
                continue;
×
381
            }
382
            $dataMirrorItem = &$dataMirror[$key];
8✔
383
            if ($dataMirrorItem instanceof Form\FieldInterface && !$dataMirrorItem->getInherit()) {
8✔
384
                unset($inheritedConfiguration[$key]);
×
385
                continue;
×
386
            }
387
            $immediateConfigurationIsArray = is_array($immediateConfiguration);
8✔
388
            $immediateSubConfiguration = $immediateConfigurationIsArray ? $immediateConfiguration[$key] ?? null : null;
8✔
389
            if ($immediateConfigurationIsArray
8✔
390
                && is_array($immediateSubConfiguration)
8✔
391
                && $inheritedValueIsSection
392
                && $this->checkIsSection($immediateConfiguration[$key])
8✔
393
            ) {
394
                // Immediate configuration is a section, and it has items. Do not allow inheritance.
395
                unset($inheritedConfiguration[$key]);
×
396
                continue;
×
397
            } elseif ($inheritedValueIsSection) {
8✔
398
                // Do not further process an inherited section - inheritance is desired.
399
                continue;
×
400
            }
401
            $this->unsetUninheritableFieldsInInheritedConfiguration(
8✔
402
                $inheritedConfiguration[$key],
8✔
403
                $immediateSubConfiguration,
8✔
404
                $dataMirrorItem,
8✔
405
            );
8✔
406
        }
407
    }
408

409
    private function checkIsSection(array $data): bool
410
    {
411
        // Determine if we are working on a section object. If so, the inherited configuration will contain an extra
412
        // dimension; an alpha-numeric unique key 22 chars in length. We can identify if that is the case by
413
        // checking that every array key is exactly 22 chars and every element contains a "_TOGGLE" key.
414
        $numberOfValues = count($data);
8✔
415
        return $numberOfValues > 0
8✔
416
            && strlen(implode('', array_keys($data))) === ($numberOfValues * 22)
8✔
417
            && count(array_column($data, '_TOGGLE')) === $numberOfValues;
8✔
418
    }
419

420
    private function extractDataStorageMirrorWithInheritableFields(
421
        Form\FieldContainerInterface $container,
422
        array &$parentArrayPosition
423
    ): void {
424
        if (!$container->getInherit()) {
8✔
425
            return;
×
426
        }
427
        foreach ($container->getChildren() as $child) {
8✔
428
            if (!$child->getInherit()) {
8✔
429
                continue;
×
430
            }
431
            $childName = $child->getName();
8✔
432
            if ($child instanceof Form\Container\Section) {
8✔
433
                foreach ($child->getChildren() as $sectionObject) {
×
434
                    /** @var Form\Container\SectionObject $sectionObject */
435
                    $sectionObjectName = $sectionObject->getName();
×
436
                    $parentArrayPosition['lDEF'][$childName]['el'][$sectionObjectName]['el'] = [];
×
437
                    $position = &$parentArrayPosition['lDEF'][$childName]['el'][$sectionObjectName]['el'];
×
438
                    foreach ($sectionObject->getChildren() as $field) {
×
439
                        $position[$field->getName()] = $field;
×
440
                    }
441
                }
442
            } elseif (!$child instanceof Form\FieldContainerInterface) {
8✔
443
                $parentArrayPosition['lDEF'][$childName]['vDEF'] = $child;
×
444
            } else {
445
                $parentArrayPosition[$childName] = [];
8✔
446
                $this->extractDataStorageMirrorWithInheritableFields($child, $parentArrayPosition[$childName]);
8✔
447
            }
448
        }
449
    }
450

451
    /**
452
     * @codeCoverageIgnore
453
     */
454
    protected function convertXmlToArray(string $xml): ?array
455
    {
456
        /** @var string|array $converted */
457
        $converted = GeneralUtility::xml2array($xml);
458
        return is_string($converted) ? null : $converted;
459
    }
460

461
    /**
462
     * Gets an inheritance tree (ordered first parent -> ... -> root record)
463
     * of record arrays containing raw values, stopping at the first parent
464
     * record that defines a page layout to use in "page layout - subpages".
465
     */
466
    protected function getInheritanceTree(array $row): array
467
    {
468
        $previousTemplate = $row[self::FIELD_ACTION_MAIN] ?? null;
16✔
469
        $configuredInheritance = ExtensionConfigurationUtility::getOption(ExtensionOption::OPTION_INHERITANCE_MODE);
16✔
470

471
        $form = $this->getForm($row);
16✔
472

473
        $defaultInheritanceMode = ($form ? $form->getOption(FormOption::INHERITANCE_MODE) : $configuredInheritance)
16✔
474
            ?? $configuredInheritance;
12✔
475

476
        $records = $this->loadRecordTreeFromDatabase($row);
16✔
477
        foreach ($records as $index => $record) {
16✔
478
            $childForm = $this->getForm($record);
12✔
479
            $subAction = $record[self::FIELD_ACTION_SUB] ?? null;
12✔
480
            $hasSubAction = !empty($subAction);
12✔
481

482
            if ($childForm) {
12✔
483
                $inheritanceMode = $childForm->getOption(FormOption::INHERITANCE_MODE) ?? $defaultInheritanceMode;
8✔
484
            } else {
485
                $inheritanceMode = $defaultInheritanceMode;
8✔
486
            }
487

488
            if ($inheritanceMode === 'restricted'
12✔
489
                && $hasSubAction
490
                && ($subAction ?? $previousTemplate) !== $previousTemplate
12✔
491
            ) {
492
                return array_slice($records, 0, $index + 1);
8✔
493
            }
494
            $previousTemplate = $subAction ?? $previousTemplate;
12✔
495
        }
496
        return $records;
8✔
497
    }
498

499
    protected function getInheritedConfiguration(array $row): array
500
    {
501
        $tableName = $this->getTableName($row);
24✔
502
        $tableFieldName = $this->getFieldName($row);
24✔
503
        $uid = $row['uid'] ?? '';
24✔
504
        $cacheKey = $tableName . $tableFieldName . $uid;
24✔
505
        if (false === isset(self::$cache[$cacheKey])) {
24✔
506
            $tree = array_reverse($this->getInheritanceTree($row), true);
20✔
507
            $data = [];
20✔
508
            foreach ($tree as $branch) {
20✔
509
                $values = $this->getFlexFormValuesSingle($branch, self::FIELD_NAME_SUB);
20✔
510
                $data = RecursiveArrayUtility::mergeRecursiveOverrule($data, $values, false, true);
20✔
511
            }
512
            self::$cache[$cacheKey] = $data;
20✔
513
        }
514
        return self::$cache[$cacheKey];
24✔
515
    }
516

517
    /**
518
     * @return mixed
519
     */
520
    protected function getInheritedPropertyValueByDottedPath(array $inheritedConfiguration, string $propertyPath)
521
    {
522
        if (true === empty($propertyPath)) {
20✔
523
            return null;
4✔
524
        } elseif (false === strpos($propertyPath, '.')) {
16✔
525
            if (isset($inheritedConfiguration[$propertyPath])) {
8✔
526
                return ObjectAccess::getProperty($inheritedConfiguration, $propertyPath);
4✔
527
            }
528
            return null;
4✔
529
        }
530
        return ObjectAccess::getPropertyPath($inheritedConfiguration, $propertyPath);
8✔
531
    }
532

533
    protected function unsetInheritedValues(Form\FormInterface $field, array $values): array
534
    {
535
        $name = $field->getName();
12✔
536
        $inherit = $field->getInherit();
12✔
537
        $inheritEmpty = $field->getInheritEmpty();
12✔
538
        $value = $values[$name] ?? null;
12✔
539
        $empty = empty($value) && !in_array($value, [0, '0'], true);
12✔
540
        if (!$inherit || ($inheritEmpty && $empty)) {
12✔
541
            unset($values[$name]);
4✔
542
        }
543
        return $values;
12✔
544
    }
545

546
    protected function loadRecordTreeFromDatabase(array $record): array
547
    {
548
        if (empty($record)) {
8✔
549
            return [];
4✔
550
        }
551
        /** @var RootlineUtility $rootLineUtility */
552
        $rootLineUtility = GeneralUtility::makeInstance(RootlineUtility::class, (int) ($record['uid'] ?? 0));
4✔
553
        return array_slice($rootLineUtility->get(), 1);
4✔
554
    }
555
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc