relationMapBuilder->buildFromStructure($fullTca); foreach (array_keys($fullTca) as $table) { $schemata[$table] = $this->build($table, $fullTca, $relationMap); } return new SchemaCollection($schemata); } /** * Builds a schema from a TCA table, if a sub-schema is requested, it will build the main schema and * all sub-schematas first. * * First builds all fields, then the schema and attach the fields, so all parts can never be * modified (except for adding sub-schema - this might be removed at some point hopefully). * * Then, resolves the sub-schema and the relevant fields for there with columnsOverrides taken into * account. * * As it is crucial to understand, parts such as FlexForms (incl. Sheet, SectionContainers and their Fields) * NEED to be resolved first, because they need to be attached. */ private function build(string $schemaName, array $fullTca, RelationMap $relationMap): TcaSchema { if (str_contains($schemaName, '.')) { // @todo: This 'if' is dead code, isn't it? [$mainSchema, $subSchema] = explode('.', $schemaName, 2); $mainSchema = $this->build($mainSchema, $fullTca, $relationMap); return $mainSchema->getSubSchema($subSchema); } // Collect all fields $allFields = []; $schemaDefinition = $fullTca[$schemaName]; foreach ($schemaDefinition['columns'] ?? [] as $fieldName => $fieldConfiguration) { try { $field = $this->fieldTypeFactory->createFieldType( $fieldName, $fieldConfiguration, $schemaName, $relationMap ); } catch (FieldTypeNotAvailableException) { continue; } $allFields[$fieldName] = $field; } $schemaConfiguration = $schemaDefinition['ctrl'] ?? []; // Store "palettes" information into the ctrl section if (is_array($schemaDefinition['palettes'] ?? null)) { $schemaConfiguration['palettes'] = $schemaDefinition['palettes']; } // Resolve all sub schemas and collect their fields while keeping the system fields $subSchemata = []; if (isset($schemaDefinition['ctrl']['type'])) { foreach ($schemaDefinition['types'] ?? [] as $subSchemaName => $subSchemaDefinition) { $subSchemaName = (string)$subSchemaName; $subSchemaFields = []; $subSchemaFieldInformation = $this->findRelevantFieldsForSubSchema($schemaDefinition, $subSchemaName); foreach ($subSchemaFieldInformation as $fieldName => $subSchemaFieldConfiguration) { try { $field = $this->fieldTypeFactory->createFieldType( $fieldName, $subSchemaFieldConfiguration, $subSchemaName, // Interesting side-note: The relations stay the same as it is not possible to modify // this for a subtype. $relationMap, $schemaName ); } catch (FieldTypeNotAvailableException) { continue; } $subSchemaFields[$fieldName] = $field; } $subSchemaFieldCollection = new FieldCollection($subSchemaFields); $subSchemata[$subSchemaName] = new TcaSchema( $schemaName . '.' . $subSchemaName, $subSchemaFieldCollection, // Merge parts from the "types" section into the ctrl section of the main schema array_replace_recursive($schemaConfiguration, $subSchemaDefinition), null, [], $this->getOrderedWizardSteps($subSchemaDefinition, $subSchemaFieldCollection, $subSchemaName) ); } } elseif (($schemaDefinition['types'] ?? []) !== []) { // Merge parts from the "types" section into the ctrl section of the main schema $schemaConfiguration = array_replace_recursive( $schemaConfiguration, array_first($schemaDefinition['types']) ); } return new TcaSchema( $schemaName, new FieldCollection($allFields), $schemaConfiguration, $subSchemata !== [] ? new SchemaCollection($subSchemata) : null, $relationMap->getPassiveRelations($schemaName) ); } private function findRelevantFieldsForSubSchema(array $tcaForTable, string $subSchemaName): array { $fields = []; if (!isset($tcaForTable['types'][$subSchemaName])) { throw new \InvalidArgumentException('Subschema "' . $subSchemaName . '" not found.', 1715269835); } $subSchemaConfig = $tcaForTable['types'][$subSchemaName]; $showItemArray = GeneralUtility::trimExplode(',', $subSchemaConfig['showitem'] ?? '', true); foreach ($showItemArray as $aShowItemFieldString) { [$fieldName, $fieldLabel, $paletteName] = GeneralUtility::trimExplode(';', $aShowItemFieldString . ';;;'); if ($fieldName === '--div--') { // tabs are not of interest here continue; } if ($fieldName === '--palette--' && !empty($paletteName)) { // showitem references to a palette field. unpack the palette and process // label overrides that may be in there. if (!isset($tcaForTable['palettes'][$paletteName]['showitem'])) { // No palette with this name found? Skip it. continue; } $palettesArray = GeneralUtility::trimExplode( ',', $tcaForTable['palettes'][$paletteName]['showitem'] ); foreach ($palettesArray as $aPalettesString) { [$fieldName, $fieldLabel] = GeneralUtility::trimExplode(';', $aPalettesString . ';;'); if (isset($tcaForTable['columns'][$fieldName])) { $fields[$fieldName] = $this->getFinalFieldConfiguration($fieldName, $tcaForTable, $subSchemaConfig, $fieldLabel); } } } elseif (isset($tcaForTable['columns'][$fieldName])) { $fields[$fieldName] = $this->getFinalFieldConfiguration($fieldName, $tcaForTable, $subSchemaConfig, $fieldLabel); } } return $fields; } /** * Handle label and possible columnsOverrides */ private function getFinalFieldConfiguration(string $fieldName, array $schemaConfiguration, array $subSchemaConfiguration, ?string $fieldLabel): array { $fieldConfiguration = $schemaConfiguration['columns'][$fieldName] ?? []; if (isset($subSchemaConfiguration['columnsOverrides'][$fieldName])) { $fieldConfiguration = array_replace_recursive($fieldConfiguration, $subSchemaConfiguration['columnsOverrides'][$fieldName]); } if (!empty($fieldLabel)) { $fieldConfiguration['label'] = $fieldLabel; } return $fieldConfiguration; } /** * @throws UndefinedFieldException */ private function getOrderedWizardSteps(array $schemaDefinition, FieldCollection $fieldCollection, string $subSchemaName): array { if (!isset($schemaDefinition['wizardSteps'])) { return []; } $wizardSteps = []; $dependencyOrderingService = GeneralUtility::makeInstance(DependencyOrderingService::class); $orderedWizardSteps = $dependencyOrderingService->orderByDependencies($schemaDefinition['wizardSteps']); foreach ($orderedWizardSteps as $stepIdentifier => $wizardStep) { $fields = $wizardStep['fields'] ?? throw new \UnexpectedValueException('Wizard step fields are missing', 1774356281); $undefinedFields = array_diff($fields, $fieldCollection->getNames()); if ($undefinedFields !== []) { throw new UndefinedFieldException(sprintf('Wizard step fields: "%s" are not configured in TCA schema: "%s"', implode(',', $undefinedFields), $subSchemaName), 1774355993); } $wizardFieldCollection = array_filter(iterator_to_array($fieldCollection), fn(FieldTypeInterface $field) => in_array($field->getName(), $fields)); $wizardSteps[$stepIdentifier] = new WizardStep($stepIdentifier, $wizardStep['title'] ?? '', new FieldCollection($wizardFieldCollection)); } return $wizardSteps; } }