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

FluidTYPO3 / flux / 12950840821

24 Jan 2025 01:44PM UTC coverage: 93.248% (-0.2%) from 93.413%
12950840821

Pull #2221

github

web-flow
Merge 2c7a7e910 into e91a963b7
Pull Request #2221: [FEATURE] Apply preview modes to page previews

30 of 44 new or added lines in 5 files covered. (68.18%)

7071 of 7583 relevant lines covered (93.25%)

65.89 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
declare(strict_types=1);
3
namespace FluidTYPO3\Flux\Provider;
4

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

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

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

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

54
    private static array $cache = [];
55

56
    protected PageService $pageService;
57

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

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

84
    public function getForm(array $row, ?string $forField = null): ?Form
85
    {
86
        if ($row['deleted'] ?? false) {
35✔
87
            return null;
14✔
88
        }
89

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

104
            $pageTemplateConfiguration = $this->pageService->getPageTemplateConfiguration(
14✔
105
                (integer) $pageUid,
14✔
106
                $pageUidIsParent
14✔
107
            );
14✔
108

109
            $form = parent::getForm($pageTemplateConfiguration['record_sub'] ?? $row, self::FIELD_NAME_SUB);
14✔
110
        } else {
111
            $form = parent::getForm($row, $forField);
7✔
112
        }
113

114
        return $form;
21✔
115
    }
116

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

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

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

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

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

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

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

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

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

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

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

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

274
            if ($before !== $stored) {
7✔
275
                $this->recordService->update((string) $tableName, $stored);
7✔
276
            }
277
        }
278

279
        return false;
7✔
280
    }
281

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

293
        $tree = array_reverse($this->getInheritanceTree($currentPageRecord), true);
14✔
294

295
        $inheritedConfiguration = [];
14✔
296

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

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

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

340
        if (is_array($configuration['databaseRow'][self::FIELD_NAME_SUB])) {
14✔
341
            $subData = $configuration['databaseRow'][self::FIELD_NAME_SUB];
7✔
342
        } else {
343
            $subData = $this->convertXmlToArray($configuration['databaseRow'][self::FIELD_NAME_SUB]) ?? [];
7✔
344
        }
345

346
        $configuration['databaseRow'][self::FIELD_NAME_SUB] = RecursiveArrayUtility::mergeRecursiveOverrule(
14✔
347
            $inheritedConfigurationForFields[self::FIELD_NAME_SUB],
14✔
348
            $subData
14✔
349
        );
14✔
350

351
        return parent::processTableConfiguration($row, $configuration);
14✔
352
    }
353

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

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

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

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

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

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

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

473
        $form = $this->getForm($row);
28✔
474

475
        $defaultInheritanceMode = ($form ? $form->getOption(FormOption::INHERITANCE_MODE) : $configuredInheritance)
28✔
476
            ?? $configuredInheritance;
22✔
477

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

484
            if ($childForm) {
21✔
485
                $inheritanceMode = $childForm->getOption(FormOption::INHERITANCE_MODE) ?? $defaultInheritanceMode;
14✔
486
            } else {
487
                $inheritanceMode = $defaultInheritanceMode;
14✔
488
            }
489

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

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

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

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

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

© 2026 Coveralls, Inc