Commit df0dcddf authored by Christian Kuhn's avatar Christian Kuhn Committed by Tymoteusz Motylewski
Browse files

[TASK] TCA tree refactoring

The patch refactors the TCA tree form engine data calculation.

The tree now works with "new" (not yet persisted) records, even if
the record has types in combination with flex forms. For instance,
a new ext:news tt_content element now renders the category tree
within flex forms, even in new tt_content records that have not been
saved.

The TCA tree no longer fetches all items when opening a record initially,
but defers that to the ajax request. This gives a massive performance
improvement on initial load of a record if the displayed TCA tree is bigger.

The ajax request itself now compiles only data of the requested TCA field,
also resulting in a significant performance improvement.

As example, ext:styleguide "elements select" is now rendered much quicker
and the single ajax calls per tree are reduced from about 4 seconds to less
than a second each with my test data.

Change-Id: If3c4c1779f5fe1510ffc13d1c9f1151bddab13e9
Resolves: #78744
Releases: master
Reviewed-on: https://review.typo3.org/50700

Reviewed-by: default avatarThomas Maroschik <tmaroschik@dfau.de>
Tested-by: default avatarThomas Maroschik <tmaroschik@dfau.de>
Reviewed-by: Tymoteusz Motylewski's avatarTymoteusz Motylewski <t.motylewski@gmail.com>
Tested-by: Tymoteusz Motylewski's avatarTymoteusz Motylewski <t.motylewski@gmail.com>
parent 20a82fc0
......@@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -35,25 +36,76 @@ class SelectTreeController
public function fetchDataAction(ServerRequestInterface $request, ResponseInterface $response)
{
$tableName = $request->getQueryParams()['table'];
if (!$this->getBackendUser()->check('tables_select', $tableName)) {
return $response;
$fieldName = $request->getQueryParams()['field'];
// Prepare processedTca: Remove all column definitions except the one that contains
// our tree definition. This way only this field is calculated, everything else is ignored.
if (!isset($GLOBALS['TCA'][$tableName]) || !is_array($GLOBALS['TCA'][$tableName])) {
throw new \RuntimeException(
'TCA for table ' . $tableName . ' not found',
1479386729
);
}
$processedTca = $GLOBALS['TCA'][$tableName];
if (!isset($processedTca['columns'][$fieldName]) || !is_array($processedTca['columns'][$fieldName])) {
throw new \RuntimeException(
'TCA for table ' . $tableName . ' and field ' . $fieldName . ' not found',
1479386990
);
}
// Force given record type and set showitem to our field only
$recordTypeValue = $request->getQueryParams()['record_type_value'];
$processedTca['types'][$recordTypeValue]['showitem'] = $fieldName;
// Unset all columns except our field
$processedTca['columns'] = [
$fieldName => $processedTca['columns'][$fieldName],
];
$flexFormPath = [];
if ($processedTca['columns'][$fieldName]['config']['type'] === 'flex') {
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructureIdentifier = json_encode($request->getQueryParams()['flex_form_datastructure_identifier']);
$dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
// Try to reduce given data structure down to the relevant element only
$flexFormPath = $request->getQueryParams()['flex_form_path'];
$fieldPattern = 'data[' . $tableName . '][';
$flexFormPath = str_replace($fieldPattern, '', $flexFormPath);
$flexFormPath = substr($flexFormPath, 0, -1);
$flexFormPath = explode('][', $flexFormPath);
if (isset($dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]])) {
$dataStructure = [
'sheets' => [
$flexFormPath[3] => [
'ROOT' => [
'type' => 'array',
'el' => [
$flexFormPath[5] => $dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]],
],
],
],
],
];
}
$processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure;
$processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
}
$formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
$formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
$formDataCompilerInput = [
'tableName' => $request->getQueryParams()['table'],
'vanillaUid' => (int)$request->getQueryParams()['uid'],
'command' => $request->getQueryParams()['command'],
'processedTca' => $processedTca,
'recordTypeValue' => $recordTypeValue,
'selectTreeCompileItems' => true,
];
$fieldName = $request->getQueryParams()['field'];
$formData = $formDataCompiler->compile($formDataCompilerInput);
if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
$flexFormFieldName = $request->getQueryParams()['flex_form_field_name'];
$value = $this->searchForFieldInFlexStructure($formData['processedTca']['columns'][$fieldName]['config'], $flexFormFieldName);
$treeData = $value['config']['treeData'];
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]]['config']['treeData'];
} else {
$treeData = $formData['processedTca']['columns'][$fieldName]['config']['treeData'];
}
......@@ -62,37 +114,4 @@ class SelectTreeController
$response->getBody()->write($json);
return $response;
}
/**
* A workaround for flexforms - there is no easy way to get flex field by key, so we need to search for it
*
* @todo remove me once flexforms are refactored
*
* @param array $array
* @param string $needle
* @return array
*/
protected function searchForFieldInFlexStructure(array $array, $needle)
{
$needle = trim($needle);
$iterator = new \RecursiveArrayIterator($array);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($recursive as $key => $value) {
if ($key === $needle) {
return $value;
}
}
return [];
}
/**
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
*/
protected function getBackendUser()
{
return $GLOBALS['BE_USER'];
}
}
......@@ -79,8 +79,6 @@ class FlexFormElementContainer extends AbstractContainer
// Set up options for single element
$fakeParameterArray = [
'fieldConf' => [
// @todo review this field during flex refactoring
'flexFormFieldName' => $flexFormFieldName,
'label' => $languageService->sL(trim($flexFormFieldArray['label'])),
'config' => $flexFormFieldArray['config'],
'children' => $flexFormFieldArray['children'],
......
......@@ -76,7 +76,12 @@ class SelectTreeElement extends AbstractFormElement
$heightInPx = $height * $this->itemHeight;
$treeWrapperId = 'tree_' . $formElementId;
$flexFormFieldName = !empty($parameterArray['fieldConf']['flexFormFieldName']) ? htmlspecialchars($parameterArray['fieldConf']['flexFormFieldName']) : '';
$fieldName = $this->data['fieldName'];
$flexDataStructureIdentifier = '';
if ($this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
$flexDataStructureIdentifier = $this->data['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'];
}
$html = [];
$html[] = '<div class="typo3-tceforms-tree">';
$html[] = ' <input class="treeRecord" type="hidden"';
......@@ -85,8 +90,9 @@ class SelectTreeElement extends AbstractFormElement
$html[] = ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
$html[] = ' data-table="' . htmlspecialchars($this->data['tableName']) . '"';
$html[] = ' data-field="' . htmlspecialchars($this->data['fieldName']) . '"';
$html[] = ' data-flex-form-field-name="' . $flexFormFieldName . '"';
$html[] = ' data-flex-form-datastructure-identifier="' . htmlspecialchars($flexDataStructureIdentifier) . '"';
$html[] = ' data-uid="' . (int)$this->data['vanillaUid'] . '"';
$html[] = ' data-recordtypevalue="' . $this->data['recordTypeValue'] . '"';
$html[] = ' data-command="' . htmlspecialchars($this->data['command']) . '"';
$html[] = ' data-read-only="' . $readOnly . '"';
$html[] = ' data-tree-exclusive-keys="' . htmlspecialchars($exclusiveKeys) . '"';
......
......@@ -190,7 +190,7 @@ class FormDataCompiler
// can be shown. This array holds those additional language records, Array key is sys_language_uid.
'additionalLanguageRows' => [],
// The tca record type value of the record. Forced to string, there can be "named" type values.
'recordTypeValue' => '0',
'recordTypeValue' => '',
// TCA of table with processed fields. After processing, this array contains merged and resolved
// array data, items were resolved, only used types are set, renderTypes are set.
'processedTca' => [],
......@@ -204,6 +204,10 @@ class FormDataCompiler
// itemsProcFunc need to have this data at hand to do their job.
'flexParentDatabaseRow' => [],
// If true, TcaSelectTreeItems data provider will compile tree items. This is false by default since
// on opening a record items are not calculated but are fetch in an ajax request, see SelectTreeController.
'selectTreeCompileItems' => false,
// BackendUser->uc['inlineView'] - This array holds status of expand / collapsed inline items
// This array is "flat", an inline structure with parent uid 1 having firstChild uid 2 having secondChild uid 3
// firstChild and secondChild are not nested. If an uid is set it means "record is expanded", example:
......
......@@ -46,6 +46,11 @@ class DatabaseRecordTypeValue implements FormDataProviderInterface
);
}
// Guard clause to suppress any calculation if record type value has been set from outside already
if ($result['recordTypeValue'] !== '') {
return $result;
}
$recordTypeValue = '0';
if (!empty($result['processedTca']['ctrl']['type'])) {
$tcaTypeField = $result['processedTca']['ctrl']['type'];
......
......@@ -30,16 +30,19 @@ class InitializeProcessedTca implements FormDataProviderInterface
*/
public function addData(array $result)
{
if (
!isset($GLOBALS['TCA'][$result['tableName']])
|| !is_array($GLOBALS['TCA'][$result['tableName']])
) {
throw new \UnexpectedValueException(
'TCA for table ' . $result['tableName'] . ' not found',
1437914223
);
if (empty($result['processedTca'])) {
if (
!isset($GLOBALS['TCA'][$result['tableName']])
|| !is_array($GLOBALS['TCA'][$result['tableName']])
) {
throw new \UnexpectedValueException(
'TCA for table ' . $result['tableName'] . ' not found',
1437914223
);
}
$result['processedTca'] = $GLOBALS['TCA'][$result['tableName']];
}
$result['processedTca'] = $GLOBALS['TCA'][$result['tableName']];
if (!is_array($result['processedTca']['columns'])) {
throw new \UnexpectedValueException(
......
......@@ -63,16 +63,21 @@ class TcaFlexPrepare implements FormDataProviderInterface
*/
protected function initializeDataStructure(array $result, $fieldName)
{
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
$result['processedTca']['columns'][$fieldName],
$result['tableName'],
$fieldName,
$result['databaseRow']
);
// Add the identifier to TCA to use it later during rendering
$result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
$dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
if (!isset($result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'])) {
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
$result['processedTca']['columns'][$fieldName],
$result['tableName'],
$fieldName,
$result['databaseRow']
);
// Add the identifier to TCA to use it later during rendering
$result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
$dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
} else {
// Assume the data structure has been given from outside if the data structure identifier is already set.
$dataStructureArray = $result['processedTca']['columns'][$fieldName]['config']['ds'];
}
if (!isset($dataStructureArray['meta']) || !is_array($dataStructureArray['meta'])) {
$dataStructureArray['meta'] = [];
}
......
......@@ -468,6 +468,7 @@ class TcaFlexProcess implements FormDataProviderInterface
$singleFieldName => $singleFieldConfiguration,
],
],
'selectTreeCompileItems' => false,
'flexParentDatabaseRow' => $result['databaseRow'],
];
$flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
......@@ -518,6 +519,8 @@ class TcaFlexProcess implements FormDataProviderInterface
'columns' => [],
],
'flexParentDatabaseRow' => $result['databaseRow'],
// Whether to compile TCA tree items - inherit from parent
'selectTreeCompileItems' => $result['selectTreeCompileItems'],
];
if (!empty($tcaNewColumns)) {
......
......@@ -46,42 +46,8 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
continue;
}
$fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName);
$fieldConfig['config']['maxitems'] = $this->sanitizeMaxItems($fieldConfig['config']['maxitems']);
$pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []);
$fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']);
$staticItems = $fieldConfig['config']['items'] + $pageTsConfigAddItems;
$fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']);
$dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems);
$fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $pageTsConfigAddItems + $fieldConfig['config']['items'];
$fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']);
// Resolve "itemsProcFunc"
if (!empty($fieldConfig['config']['itemsProcFunc'])) {
$fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']);
// itemsProcFunc must not be used anymore
unset($fieldConfig['config']['itemsProcFunc']);
}
// Translate labels
$fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName);
$staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems);
$result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName);
$result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues);
// Keys may contain table names, so a numeric array is created
$fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']);
// A couple of tree specific config parameters can be overwritten via page TS.
// Pick those that influence the data fetching and write them into the config
// given to the tree data provider
......@@ -102,7 +68,44 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
}
}
$fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems);
if ($result['selectTreeCompileItems']) {
$fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName);
$pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []);
$fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']);
$staticItems = $fieldConfig['config']['items'] + $pageTsConfigAddItems;
$fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']);
$dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems);
$fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $pageTsConfigAddItems + $fieldConfig['config']['items'];
$fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']);
$fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']);
// Resolve "itemsProcFunc"
if (!empty($fieldConfig['config']['itemsProcFunc'])) {
$fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']);
// itemsProcFunc must not be used anymore
unset($fieldConfig['config']['itemsProcFunc']);
}
// Translate labels
$fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName);
$staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems);
$result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName);
$result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues);
// Keys may contain table names, so a numeric array is created
$fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']);
$fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems);
}
$result['processedTca']['columns'][$fieldName] = $fieldConfig;
}
......@@ -151,7 +154,6 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
$treeConfig = [
'items' => $treeItems,
'selectedNodes' => $this->prepareSelectedNodes($fieldConfig['config']['items'], $result['databaseRow'][$fieldName])
];
return $treeConfig;
......@@ -186,31 +188,6 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
return $additionalItems;
}
/**
* Make sure to only keep the selected nodes that are really available in the database and for the user
* (e.g. after permissions etc)
*
* @param array $itemArray
* @param array $databaseValues
* @return array
* @todo: this is ugly - should be removed with the tree rewrite
*/
protected function prepareSelectedNodes(array $itemArray, array $databaseValues)
{
$selectedNodes = [];
if (!empty($databaseValues)) {
foreach ($databaseValues as $selectedNode) {
foreach ($itemArray as $possibleSelectBoxItem) {
if ((string)$possibleSelectBoxItem[1] === (string)$selectedNode) {
$selectedNodes[] = $selectedNode;
}
}
}
}
return $selectedNodes;
}
/**
* Determines whether the current field is a valid target for this DataProvider
*
......
......@@ -33,7 +33,9 @@ define(['jquery', 'TYPO3/CMS/Backend/FormEngine/Element/SelectTree'], function (
table: treeInput.data('table'),
field: treeInput.data('field'),
uid: treeInput.data('uid'),
flex_form_field_name: treeInput.data('flex-form-field-name'),
record_type_value: treeInput.data('recordtypevalue'),
flex_form_datastructure_identifier: treeInput.data('flex-form-datastructure-identifier'),
flex_form_path: treeInput.data('formengine-input-name'),
command: treeInput.data('command')
};
var $wrapper = treeInput.parent().siblings('.svg-tree-wrapper');
......
<?php
namespace TYPO3\CMS\Backend\Tests\Unit\Controller;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Controller\SelectTreeController;
use TYPO3\CMS\Core\Tests\UnitTestCase;
/**
* Test case
*/
class SelectTreeControllerTest extends UnitTestCase
{
/**
* @test
*/
public function fetchDataActionThrowsExceptionIfTcaOfTableDoesNotExist()
{
$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$responseProphecy = $this->prophesize(ResponseInterface::class);
$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1479386729);
(new SelectTreeController())->fetchDataAction($requestProphecy->reveal(), $responseProphecy->reveal());
}
/**
* @test
*/
public function fetchDataActionThrowsExceptionIfTcaOfTableFieldDoesNotExist()
{
$responseProphecy = $this->prophesize(ResponseInterface::class);
$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$requestProphecy->getQueryParams()->shouldBeCalled()->willReturn([
'table' => 'aTable',
'field' => 'aField',
]);
$GLOBALS['TCA']['aTable']['columns'] = [];
$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1479386990);
(new SelectTreeController())->fetchDataAction($requestProphecy->reveal(), $responseProphecy->reveal());
}
}
......@@ -58,12 +58,47 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
$this->subject->addData($input);
}
/**
* @test
*/
public function addDataKeepsExistingTcaRecordTypeValue()
{
$input = [
'recordTypeValue' => 'egon',
'processedTca' => [
'types' => [
'1' => 'foo',
],
],
];
$expected = $input;
$this->assertSame($expected, $this->subject->addData($input));
}
/**
* @test
*/
public function addDataKeepsExistingTcaRecordTypeValueWithValueZero()
{
$input = [
'recordTypeValue' => 0,
'processedTca' => [
'types' => [
'1' => 'foo',
],
],
];
$expected = $input;
$this->assertSame($expected, $this->subject->addData($input));
}
/**
* @test
*/
public function addDataSetsRecordTypeValueToHistoricalOneIfTypeZeroIsNotDefined()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'types' => [
'1' => 'foo',
......@@ -81,6 +116,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataSetsRecordTypeValueToZero()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'types' => [
'0' => 'foo',
......@@ -100,6 +136,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataThrowsExceptionIfTypePointsToANotExistingField()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'ctrl' => [
'type' => 'notExists',
......@@ -125,6 +162,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataSetsRecordTypeValueToValueOfDatabaseField()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'ctrl' => [
'type' => 'aField',
......@@ -150,6 +188,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataSetsRecordTypeValueToZeroIfValueOfDatabaseFieldIsNotDefinedInTca()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'ctrl' => [
'type' => 'aField',
......@@ -175,6 +214,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataSetsRecordTypeValueToZeroIfValueOfDatabaseFieldIsEmptyString()
{
$input = [
'recordTypeValue' => '',
'processedTca' => [
'ctrl' => [
'type' => 'aField',
......@@ -200,6 +240,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase
public function addDataThrowsExceptionIfValueTypesNotExistsAndNoFallbackExists()
{