.. include:: /Includes.rst.txt

.. _feature-104002-1718273913:

=============================
Feature: #104002 - Schema API
=============================

See :issue:`104002`

Description
===========

A new Schema API is introduced to access information about TCA structures
in a unified way.

The main goal of this architecture is to reduce direct access to
:php:`$GLOBALS['TCA']` after the Bootstrap process is completed.

The Schema API implements the following design goals:

#.  An object-oriented approach to access common TCA information such as if a
    database table is localizable or workspace-aware, if it has a "deleted" field
    ("soft-delete"), or other common functionality such as "enableFields" / "enablecolumns",
    which can be accessed via "Capabilities" within a Schema.

#.  A unified way to access information which "types" a TCA table has available,
    such as "tt_content", where the "CType" field is the divisor for types, thus,
    allowing a Schema to have sub-schemata for a TCA Table.

    The API in turn then handles which fields are available for a specific "CType".
    An example is "tt_content" with type "textpic": The sub-schema "tt_content.textpic"
    only contains the fields that are registered of that "CType", such as "bodytext",
    which then knows it is a Rich Text Field (the default column does not have this information),
    or "image" (a file relation field), but the sub-schema does not contain fields
    that are irrelevant for this type, such as "assets" (also a file relation field).

#.  An abstracted approach to available TCA field types such as "input" or "select",
    which also takes information into account, if a select field is a selection of a
    static list (such as "pages.layout") or if it contains a relation to another
    schema or field (based on "foreign_table"). Previously, this was evaluated in
    many places in TYPO3 Core, and can now be reduced.

    Thus, Schema API can now be utilized to determine the :php:`RelationshipType`
    of a relational field type in a unified way without having to deal with deeply
    nested arrays.

#.  Information about relations to other database tables or fields. This is
    especially useful when dealing with Inline elements or category selection fields.

    Schema API can find out, which fields of other schemata are pointing to one-self.
    Schema API differentiates between an "Active Relation" and a "Passive Relation".
    An Active Relation is the information that a field such as "pages.media"
    (a field of type "file") contains a reference to the "sys_file_reference.uid_foreign"
    field. Active Relations in consequence are connected to a specific field
    (of type :php:`RelationalFieldTypeInterface`).

    In turn, a "Passive Relation" is the information what other schemata/fields are
    pointing to a specific table or field.

    A common example of a "Passive Relation" is "sys_workspace_stage":
    The information stored in :php:`$GLOBALS[TCA][sys_workspace_stage]` does not contain
    the information that this table is actually used as a reference from the database
    field `sys_workspace.custom_stages`, the `sys_workspace_stage` Schema now
    contains this information directly via :php:`TcaSchema->getPassiveRelations()`.
    This is possible as TcaSchemaFactory is evaluating all TCA information and
    holistically as a graph. Passive Relations
    are currently only connected to a Schema, and Active Relations to a Field or
    a Schema.

    As the Schema API fetches information solely based on the TCA, an Active Relation
    only points to **possible** references, however, the actual reference
    (does a record really have a connection to another database table) would
    require an actual Record instance (a database row) to evaluate this information.

    Relations do not know about the "Type" or "Quantity" (many-to-many etc) as
    this information is already stored in the Field information. For this reason,
    the "Relations" currently only contain a flat information structure of the table
    (and possibly a field) pointing TO another schema name (Active Relation) or
    FROM another schema name / field (Passive Relation).

    Schema API also parses all available FlexForm data structures in order to
    resolve relations. As a result, a field of type FlexFormField contains
    a list of possible "FlexFormSchema" instances, which resolve all fields, sheets
    and section containers within each data structure.

#.  Once built, the Schema can never be changed. Whereas the TCA could be
    overridden at runtime, all TCA is now evaluated once and
    then cached. This is a consequence of working with an object-oriented approach.

    If the TCA is changed after the Bootstrap process is completed,
    the Schema needs to be rebuilt manually, which TYPO3 Core currently does, for
    example, in some Functional Testing Scenarios.

    All key objects (Schema, FieldType, Capabilities) are treated as immutable DTOs
    and never contain cross-references to their parent objects (Sub schemata do not
    know information about their parent schema, a field does not know which schema
    it belongs to), so the only entry point is always the :php:`TcaSchemaFactory`
    object.

    This design allows the API to be fully cacheable at PHP level as a nested tree.

#.  Low-level, not full-fledged but serves as a basis.

    Several API decisions were made in order to let Schema API keep only its
    original purpose, but can be encapsulated further in other APIs:

    -   Schema API is not available during Bootstrap as it needs TCA to be available
        and fully finished.

    -   Schema API does not contain all available TCA properties for each field type.
        An example is "renderType" for select fields. This information is not relevant
        when querying records in the frontend, and mainly relevant for FormEngine -
        it is not generic enough to justify a getter method.

    -   Extensibility: custom field types are currently not available
        until TYPO3 Core as fully migrated to Schema API.

    -   User Permissions: Evaluating if a user has access to "tt_content.bodytext"
        requires information about the currently logged in user, thus it is not part of the
        Schema API. A "Permission API" should evaluate this information in the
        future.

    -   Available options for a field. As an example, a common scenario is to find out
        which possible options are available for "pages.backend_layout". In TYPO3 Core
        an :php:`itemsProcFunc` is connected to that field in TCA. Whether there is an
        :php:`itemsProcFunc` is stored, but Schema API is not designed to actually
        execute the itemsProcFunc as it is dependent on various factors evaluated during
        runtime, such as the page it resides on, user permissions or PageTsConfig
        overrides.

    Schema API is currently marked as internal, as it might be changed during
    TYPO3 v13 development, because more parts of TYPO3 will be migrated towards
    Schema API.

    DataHandler and the Record Factory already utilize Schema API in order to reduce
    direct access to :php:`$GLOBALS[TCA]`.

    In the future Schema API might be used to evaluate information
    for Site Configurations, like TCA and FlexForms.

Impact
======

Reading and writing :php:`$GLOBALS[TCA]` within :file:`Configuration/TCA/*`
and via TCA Overrides is untouched, as the API is meant for reading the
information there in a unified way.

Usage
-----

The API can now be used to find out information about TCA fields.

..  code-block:: php

    public function __construct(
        protected readonly PageRepository $pageRepository,
        protected readonly TcaSchemaFactory $tcaSchemaFactory
    ) {}

    public function myMethod(string $tableName): void
    {
        if (!$this->tcaSchemaFactory->has($tableName)) {
            // this table is not managed via TYPO3's TCA API
            return;
        }
        $schema = $this->tcaSchemaFactory->get($tableName);

        // Find out if a table is localizable
        if ($schema->isLocalizable()) {
            // do something
        }

        // Find all registered types
        $types = $schema->getSubSchemata();

    }

Using the API improves handling for parts such as evaluating :php:`columnsOverrides`,
foreign field structures, FlexForm Schema parsing, and evaluating type fields
for database fields.

.. index:: PHP-API, TCA, ext:core
