From 235c1c974eb155328ae58d45947f41abeb568cf9 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Tue, 27 Aug 2024 11:16:35 +0200
Subject: [PATCH] [TASK] Clean up TcaSchema field types

Clean up of field types for TcaSchema.

In detail:

* Removes duplicate interface implementation
* Move __set_state() to abstract
* Introduces FieldFormat enum
* Extends some FieldType's for convenience methods
* Extends TcaSchema::getFields() to support list of fields
* Adds styleguide example for type=none with format configuration

Resolves: #104741
Releases: main
Change-Id: I06afc6d52eb0f5fa299ffef5701f178378f7c942
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/85786
Tested-by: core-ci <typo3@b13.com>
Tested-by: Jochen Roth <rothjochen@gmail.com>
Tested-by: Benjamin Kott <benjamin.kott@outlook.com>
Reviewed-by: Jochen Roth <rothjochen@gmail.com>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Benjamin Kott <benjamin.kott@outlook.com>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
---
 .../Schema/Field/AbstractFieldType.php        |   6 +
 .../Schema/Field/CategoryFieldType.php        |  14 +--
 .../Schema/Field/CheckboxFieldType.php        |   7 +-
 .../Classes/Schema/Field/ColorFieldType.php   |   7 +-
 .../Schema/Field/DateTimeFieldType.php        |  12 +-
 .../Classes/Schema/Field/EmailFieldType.php   |   7 +-
 .../Classes/Schema/Field/FieldCollection.php  |  10 +-
 .../Classes/Schema/Field/FileFieldType.php    |  22 +++-
 .../Schema/Field/FlexFormFieldType.php        |  11 +-
 .../Classes/Schema/Field/FolderFieldType.php  |   7 +-
 .../Classes/Schema/Field/GroupFieldType.php   |   7 +-
 .../Field/ImageManipulationFieldType.php      |   7 +-
 .../Classes/Schema/Field/InlineFieldType.php  |   9 +-
 .../Classes/Schema/Field/InputFieldType.php   |   7 +-
 .../Classes/Schema/Field/JsonFieldType.php    |   7 +-
 .../Schema/Field/LanguageFieldType.php        |   7 +-
 .../Classes/Schema/Field/LinkFieldType.php    |   7 +-
 .../Classes/Schema/Field/NoneFieldType.php    |   8 +-
 .../Classes/Schema/Field/NumberFieldType.php  |   7 +-
 .../Schema/Field/PassthroughFieldType.php     |   7 +-
 .../Schema/Field/PasswordFieldType.php        |   7 +-
 .../Classes/Schema/Field/RadioFieldType.php   |   7 +-
 .../Schema/Field/SelectRelationFieldType.php  |   7 +-
 .../Classes/Schema/Field/SlugFieldType.php    |  11 +-
 .../Schema/Field/StaticSelectFieldType.php    |  14 ++-
 .../Classes/Schema/Field/TextFieldType.php    |   7 +-
 .../Classes/Schema/Field/UserFieldType.php    |   6 +-
 .../Classes/Schema/Field/UuidFieldType.php    |   6 +-
 .../core/Classes/Schema/FieldFormat.php       | 112 ++++++++++++++++++
 .../sysext/core/Classes/Schema/TcaSchema.php  |  10 +-
 .../TCA/tx_styleguide_elements_basic.php      |  14 ++-
 typo3/sysext/styleguide/ext_tables.sql        |   1 +
 32 files changed, 220 insertions(+), 158 deletions(-)
 create mode 100644 typo3/sysext/core/Classes/Schema/FieldFormat.php

diff --git a/typo3/sysext/core/Classes/Schema/Field/AbstractFieldType.php b/typo3/sysext/core/Classes/Schema/Field/AbstractFieldType.php
index ed7a1fa3371d..b8fe411e6403 100644
--- a/typo3/sysext/core/Classes/Schema/Field/AbstractFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/AbstractFieldType.php
@@ -29,6 +29,12 @@ abstract readonly class AbstractFieldType implements FieldTypeInterface
         protected array $configuration,
     ) {}
 
+    public static function __set_state(array $state): self
+    {
+        /** @phpstan-ignore-next-line Usage is safe because state is exported by PHP var_export() from the static instance */
+        return new static(...$state);
+    }
+
     abstract public function getType(): string;
 
     public function getName(): string
diff --git a/typo3/sysext/core/Classes/Schema/Field/CategoryFieldType.php b/typo3/sysext/core/Classes/Schema/Field/CategoryFieldType.php
index 59da0b647d6e..fb0f7feec554 100644
--- a/typo3/sysext/core/Classes/Schema/Field/CategoryFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/CategoryFieldType.php
@@ -22,12 +22,12 @@ use TYPO3\CMS\Core\Schema\RelationshipType;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class CategoryFieldType extends AbstractFieldType implements FieldTypeInterface, RelationalFieldTypeInterface
+final readonly class CategoryFieldType extends AbstractFieldType implements RelationalFieldTypeInterface
 {
     public function __construct(
         protected string $name,
         protected array $configuration,
-        protected array $relations,
+        protected array $relations
     ) {}
 
     public function getType(): string
@@ -35,6 +35,11 @@ final readonly class CategoryFieldType extends AbstractFieldType implements Fiel
         return 'category';
     }
 
+    public function getTreeConfiguration(): array
+    {
+        return $this->configuration['treeConfig'] ?? [];
+    }
+
     public function getRelations(): array
     {
         return $this->relations;
@@ -44,9 +49,4 @@ final readonly class CategoryFieldType extends AbstractFieldType implements Fiel
     {
         return RelationshipType::fromTcaConfiguration($this->configuration);
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/CheckboxFieldType.php b/typo3/sysext/core/Classes/Schema/Field/CheckboxFieldType.php
index 423aeca59ba2..6b83056a7507 100644
--- a/typo3/sysext/core/Classes/Schema/Field/CheckboxFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/CheckboxFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class CheckboxFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class CheckboxFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'check';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/ColorFieldType.php b/typo3/sysext/core/Classes/Schema/Field/ColorFieldType.php
index 335fe8342d9f..eafb6c7c7df7 100644
--- a/typo3/sysext/core/Classes/Schema/Field/ColorFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/ColorFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class ColorFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class ColorFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'color';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php b/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
index 18e1824b83c1..b46459799f96 100644
--- a/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
@@ -22,7 +22,7 @@ use TYPO3\CMS\Core\Database\Query\QueryHelper;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class DateTimeFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class DateTimeFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
@@ -38,14 +38,4 @@ final readonly class DateTimeFieldType extends AbstractFieldType implements Fiel
     {
         return in_array($this->configuration['dbType'] ?? null, QueryHelper::getDateTimeTypes(), true) ? $this->configuration['dbType'] : null;
     }
-
-    public function isNullable(): bool
-    {
-        return (bool)($this->configuration['nullable'] ?? false);
-    }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/EmailFieldType.php b/typo3/sysext/core/Classes/Schema/Field/EmailFieldType.php
index 9012ea0616d5..413f14517c48 100644
--- a/typo3/sysext/core/Classes/Schema/Field/EmailFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/EmailFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class EmailFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class EmailFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'email';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/FieldCollection.php b/typo3/sysext/core/Classes/Schema/Field/FieldCollection.php
index e462968130ae..1f7fabfa342b 100644
--- a/typo3/sysext/core/Classes/Schema/Field/FieldCollection.php
+++ b/typo3/sysext/core/Classes/Schema/Field/FieldCollection.php
@@ -29,6 +29,11 @@ final readonly class FieldCollection implements \ArrayAccess, \IteratorAggregate
         protected array $fieldDefinitions = []
     ) {}
 
+    public static function __set_state(array $state): self
+    {
+        return new self(...$state);
+    }
+
     public function offsetExists(mixed $offset): bool
     {
         return isset($this->fieldDefinitions[$offset]);
@@ -61,9 +66,4 @@ final readonly class FieldCollection implements \ArrayAccess, \IteratorAggregate
     {
         return count($this->fieldDefinitions);
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/FileFieldType.php b/typo3/sysext/core/Classes/Schema/Field/FileFieldType.php
index 15449a705506..d4e2973f3b14 100644
--- a/typo3/sysext/core/Classes/Schema/Field/FileFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/FileFieldType.php
@@ -18,6 +18,7 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Core\Schema\Field;
 
 use TYPO3\CMS\Core\Schema\RelationshipType;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * This is a field to a "file" (which is very similar to "inline") but with a hard-coded
@@ -25,7 +26,7 @@ use TYPO3\CMS\Core\Schema\RelationshipType;
  *
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class FileFieldType extends AbstractFieldType implements FieldTypeInterface, RelationalFieldTypeInterface
+final readonly class FileFieldType extends AbstractFieldType implements RelationalFieldTypeInterface
 {
     public function __construct(
         protected string $name,
@@ -38,6 +39,20 @@ final readonly class FileFieldType extends AbstractFieldType implements FieldTyp
         return 'file';
     }
 
+    public function getAllowedFileExtensions(): array
+    {
+        return is_array($this->configuration['allowed'] ?? null)
+            ? $this->configuration['allowed']
+            : GeneralUtility::trimExplode(',', $this->configuration['allowed'] ?? '', true);
+    }
+
+    public function getDisallowedFileExtensions(): array
+    {
+        return is_array($this->configuration['disallowed'] ?? null)
+            ? $this->configuration['disallowed']
+            : GeneralUtility::trimExplode(',', $this->configuration['disallowed'] ?? '', true);
+    }
+
     public function getRelations(): array
     {
         return $this->relations;
@@ -47,9 +62,4 @@ final readonly class FileFieldType extends AbstractFieldType implements FieldTyp
     {
         return RelationshipType::fromTcaConfiguration($this->configuration);
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/FlexFormFieldType.php b/typo3/sysext/core/Classes/Schema/Field/FlexFormFieldType.php
index 0f9452fe0f3e..e16c068bc7ec 100644
--- a/typo3/sysext/core/Classes/Schema/Field/FlexFormFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/FlexFormFieldType.php
@@ -20,20 +20,15 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class FlexFormFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class FlexFormFieldType extends AbstractFieldType
 {
-    public function __construct(
-        protected string $name,
-        protected array $configuration,
-    ) {}
-
     public function getType(): string
     {
         return 'flex';
     }
 
-    public static function __set_state(array $state): self
+    public function getDataStructure(): array
     {
-        return new self(...$state);
+        return is_array($this->configuration['ds'] ?? null) ? $this->configuration['ds'] : [];
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/FolderFieldType.php b/typo3/sysext/core/Classes/Schema/Field/FolderFieldType.php
index 9c39e454800e..c9248594db89 100644
--- a/typo3/sysext/core/Classes/Schema/Field/FolderFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/FolderFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class FolderFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class FolderFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'folder';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/GroupFieldType.php b/typo3/sysext/core/Classes/Schema/Field/GroupFieldType.php
index e6ffa69fac06..c46d4d25b450 100644
--- a/typo3/sysext/core/Classes/Schema/Field/GroupFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/GroupFieldType.php
@@ -22,7 +22,7 @@ use TYPO3\CMS\Core\Schema\RelationshipType;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class GroupFieldType extends AbstractFieldType implements FieldTypeInterface, RelationalFieldTypeInterface
+final readonly class GroupFieldType extends AbstractFieldType implements RelationalFieldTypeInterface
 {
     public function __construct(
         protected string $name,
@@ -44,9 +44,4 @@ final readonly class GroupFieldType extends AbstractFieldType implements FieldTy
     {
         return RelationshipType::fromTcaConfiguration($this->configuration);
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/ImageManipulationFieldType.php b/typo3/sysext/core/Classes/Schema/Field/ImageManipulationFieldType.php
index 31da630864b6..a003e285bb58 100644
--- a/typo3/sysext/core/Classes/Schema/Field/ImageManipulationFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/ImageManipulationFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class ImageManipulationFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class ImageManipulationFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'imageManipulation';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/InlineFieldType.php b/typo3/sysext/core/Classes/Schema/Field/InlineFieldType.php
index f36021e54385..140a4a1aea80 100644
--- a/typo3/sysext/core/Classes/Schema/Field/InlineFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/InlineFieldType.php
@@ -24,12 +24,12 @@ use TYPO3\CMS\Core\Schema\RelationshipType;
  *
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class InlineFieldType extends AbstractFieldType implements FieldTypeInterface, RelationalFieldTypeInterface
+final readonly class InlineFieldType extends AbstractFieldType implements RelationalFieldTypeInterface
 {
     public function __construct(
         protected string $name,
         protected array $configuration,
-        protected array $relations,
+        protected array $relations
     ) {}
 
     public function getType(): string
@@ -51,9 +51,4 @@ final readonly class InlineFieldType extends AbstractFieldType implements FieldT
     {
         return (bool)($this->configuration['behaviour']['disableMovingChildrenWithParent'] ?? false) === false;
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/InputFieldType.php b/typo3/sysext/core/Classes/Schema/Field/InputFieldType.php
index 1f252b89f37d..c00516a39a32 100644
--- a/typo3/sysext/core/Classes/Schema/Field/InputFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/InputFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class InputFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class InputFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'input';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/JsonFieldType.php b/typo3/sysext/core/Classes/Schema/Field/JsonFieldType.php
index 72a4a1b5ed59..815f9ae3bda9 100644
--- a/typo3/sysext/core/Classes/Schema/Field/JsonFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/JsonFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class JsonFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class JsonFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'json';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/LanguageFieldType.php b/typo3/sysext/core/Classes/Schema/Field/LanguageFieldType.php
index 5b695b2f739f..ba0d524f0ad7 100644
--- a/typo3/sysext/core/Classes/Schema/Field/LanguageFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/LanguageFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class LanguageFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class LanguageFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'language';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/LinkFieldType.php b/typo3/sysext/core/Classes/Schema/Field/LinkFieldType.php
index 206221914252..d42a8a8615c1 100644
--- a/typo3/sysext/core/Classes/Schema/Field/LinkFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/LinkFieldType.php
@@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class LinkFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class LinkFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
@@ -31,9 +31,4 @@ final readonly class LinkFieldType extends AbstractFieldType implements FieldTyp
     {
         return $this->configuration['allowedTypes'] ?? ['*'];
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/NoneFieldType.php b/typo3/sysext/core/Classes/Schema/Field/NoneFieldType.php
index a64783c5e21d..caf5283bfd8f 100644
--- a/typo3/sysext/core/Classes/Schema/Field/NoneFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/NoneFieldType.php
@@ -17,18 +17,20 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Schema\Field;
 
+use TYPO3\CMS\Core\Schema\FieldFormat;
+
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class NoneFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class NoneFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'none';
     }
 
-    public static function __set_state(array $state): self
+    public function getFormat(): FieldFormat
     {
-        return new self(...$state);
+        return FieldFormat::fromTcaConfiguration($this->configuration);
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/NumberFieldType.php b/typo3/sysext/core/Classes/Schema/Field/NumberFieldType.php
index d41866864e39..051bf8263f6c 100644
--- a/typo3/sysext/core/Classes/Schema/Field/NumberFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/NumberFieldType.php
@@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class NumberFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class NumberFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
@@ -31,9 +31,4 @@ final readonly class NumberFieldType extends AbstractFieldType implements FieldT
     {
         return $this->configuration['format'] ?? '';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/PassthroughFieldType.php b/typo3/sysext/core/Classes/Schema/Field/PassthroughFieldType.php
index e309344375be..ddbad6ee82b1 100644
--- a/typo3/sysext/core/Classes/Schema/Field/PassthroughFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/PassthroughFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class PassthroughFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class PassthroughFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'passthrough';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/PasswordFieldType.php b/typo3/sysext/core/Classes/Schema/Field/PasswordFieldType.php
index 318b654ae675..85c5fdf9c470 100644
--- a/typo3/sysext/core/Classes/Schema/Field/PasswordFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/PasswordFieldType.php
@@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class PasswordFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class PasswordFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
@@ -31,9 +31,4 @@ final readonly class PasswordFieldType extends AbstractFieldType implements Fiel
     {
         return $this->configuration['hashed'] ?? true;
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/RadioFieldType.php b/typo3/sysext/core/Classes/Schema/Field/RadioFieldType.php
index 32b63d576d08..ec39a06d501d 100644
--- a/typo3/sysext/core/Classes/Schema/Field/RadioFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/RadioFieldType.php
@@ -20,15 +20,10 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class RadioFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class RadioFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'radio';
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/SelectRelationFieldType.php b/typo3/sysext/core/Classes/Schema/Field/SelectRelationFieldType.php
index d43dd66cba2a..c633af078398 100644
--- a/typo3/sysext/core/Classes/Schema/Field/SelectRelationFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/SelectRelationFieldType.php
@@ -24,7 +24,7 @@ use TYPO3\CMS\Core\Schema\RelationshipType;
  *
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class SelectRelationFieldType extends AbstractFieldType implements FieldTypeInterface, RelationalFieldTypeInterface
+final readonly class SelectRelationFieldType extends AbstractFieldType implements RelationalFieldTypeInterface
 {
     public function __construct(
         protected string $name,
@@ -46,9 +46,4 @@ final readonly class SelectRelationFieldType extends AbstractFieldType implement
     {
         return RelationshipType::fromTcaConfiguration($this->configuration);
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/SlugFieldType.php b/typo3/sysext/core/Classes/Schema/Field/SlugFieldType.php
index e2998a99b8cf..0cb5c678b9c4 100644
--- a/typo3/sysext/core/Classes/Schema/Field/SlugFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/SlugFieldType.php
@@ -20,15 +20,20 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class SlugFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class SlugFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'slug';
     }
 
-    public static function __set_state(array $state): self
+    public function getGeneratorOption(string $optionName): array|string|bool|null
     {
-        return new self(...$state);
+        return $this->configuration['generatorOptions'][$optionName] ?? null;
+    }
+
+    public function getGeneratorOptions(): array
+    {
+        return is_array($this->configuration['generatorOptions']) ? $this->configuration['generatorOptions'] : [];
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/StaticSelectFieldType.php b/typo3/sysext/core/Classes/Schema/Field/StaticSelectFieldType.php
index a12f0a06eaf5..65c1c829a04d 100644
--- a/typo3/sysext/core/Classes/Schema/Field/StaticSelectFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/StaticSelectFieldType.php
@@ -17,20 +17,28 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Schema\Field;
 
+use TYPO3\CMS\Core\Schema\Struct\SelectItem;
+
 /**
  * This is a select type without any MM or foreign table logic.
  *
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class StaticSelectFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class StaticSelectFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'select';
     }
 
-    public static function __set_state(array $state): self
+    /**
+     * @return SelectItem[]
+     */
+    public function getItems(): array
     {
-        return new self(...$state);
+        return is_array($this->configuration['items'] ?? false) ? array_map(
+            static fn($item): SelectItem => SelectItem::fromTcaItemArray($item),
+            $this->configuration['items']
+        ) : [];
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/TextFieldType.php b/typo3/sysext/core/Classes/Schema/Field/TextFieldType.php
index ca3a479961c4..ab2c684c02d5 100644
--- a/typo3/sysext/core/Classes/Schema/Field/TextFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/TextFieldType.php
@@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class TextFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class TextFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
@@ -31,9 +31,4 @@ final readonly class TextFieldType extends AbstractFieldType implements FieldTyp
     {
         return $this->configuration['enableRichtext'] ?? false;
     }
-
-    public static function __set_state(array $state): self
-    {
-        return new self(...$state);
-    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/UserFieldType.php b/typo3/sysext/core/Classes/Schema/Field/UserFieldType.php
index 93ba6caa5006..bcb5aa70af5c 100644
--- a/typo3/sysext/core/Classes/Schema/Field/UserFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/UserFieldType.php
@@ -20,15 +20,15 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class UserFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class UserFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'user';
     }
 
-    public static function __set_state(array $state): self
+    public function getRenderType(): string
     {
-        return new self(...$state);
+        return $this->configuration['renderType'] ?? '';
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/UuidFieldType.php b/typo3/sysext/core/Classes/Schema/Field/UuidFieldType.php
index f8ad82a24bc7..a567ee4c49c3 100644
--- a/typo3/sysext/core/Classes/Schema/Field/UuidFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/UuidFieldType.php
@@ -20,15 +20,15 @@ namespace TYPO3\CMS\Core\Schema\Field;
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
-final readonly class UuidFieldType extends AbstractFieldType implements FieldTypeInterface
+final readonly class UuidFieldType extends AbstractFieldType
 {
     public function getType(): string
     {
         return 'uuid';
     }
 
-    public static function __set_state(array $state): self
+    public function getVersion(): int
     {
-        return new self(...$state);
+        return in_array($this->configuration['version'] ?? 0, [4, 6, 7], true) ? $this->configuration['version'] : 4;
     }
 }
diff --git a/typo3/sysext/core/Classes/Schema/FieldFormat.php b/typo3/sysext/core/Classes/Schema/FieldFormat.php
new file mode 100644
index 000000000000..305d25dd6454
--- /dev/null
+++ b/typo3/sysext/core/Classes/Schema/FieldFormat.php
@@ -0,0 +1,112 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * 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!
+ */
+
+namespace TYPO3\CMS\Core\Schema;
+
+/**
+ * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
+ */
+enum FieldFormat: string
+{
+    case Date = 'date';
+
+    case Datetime = 'datetime';
+
+    case Time = 'time';
+
+    case Timesec = 'timesec';
+
+    case Year = 'year';
+
+    case Int = 'int';
+
+    case Float = 'float';
+
+    case Number = 'number';
+
+    case Md5 = 'md5';
+
+    case Filesize = 'filesize';
+
+    case User = 'user';
+
+    case Undefined = '';
+
+    private const FORMAT_CONFIGURATION = [
+        self::Date->value => [
+            'strftime',
+            'option',
+            'appendAge',
+        ],
+        self::Int->value => [
+            'base',
+        ],
+        self::Float->value => [
+            'precision',
+        ],
+        self::Number->value => [
+            'option',
+        ],
+        self::Filesize->value => [
+            'appendByteSize',
+        ],
+        self::User->value => [
+            'userFunc',
+        ],
+    ];
+
+    public static function fromTcaConfiguration(array $configuration): self
+    {
+        if (isset($configuration['config'])) {
+            $configuration = $configuration['config'];
+        }
+        if (isset($configuration['format'])) {
+            return match ($configuration['format']) {
+                'date' => self::Date,
+                'datetime' => self::Datetime,
+                'time' => self::Time,
+                'timesec' => self::Timesec,
+                'year' => self::Year,
+                'int' => self::Int,
+                'float' => self::Float,
+                'number' => self::Number,
+                'md5' => self::Md5,
+                'filesize' => self::Filesize,
+                'user' => self::User,
+                default => throw new \UnexpectedValueException('Invalid format: ' . $configuration['format'], 1724744407),
+            };
+        }
+
+        return self::Undefined;
+    }
+
+    public function getFormatConfiguration(array $configuration): array
+    {
+        if (isset($configuration['config'])) {
+            $configuration = $configuration['config'];
+        }
+
+        if (!isset(self::FORMAT_CONFIGURATION[$this->value])
+            || !is_array($configuration['format.'] ?? false)
+            || $configuration['format.'] === []
+        ) {
+            return [];
+        }
+
+        return array_filter($configuration['format.'], fn(string $option): bool => in_array($option, self::FORMAT_CONFIGURATION[$this->value], true), ARRAY_FILTER_USE_KEY);
+    }
+}
diff --git a/typo3/sysext/core/Classes/Schema/TcaSchema.php b/typo3/sysext/core/Classes/Schema/TcaSchema.php
index 739c7d15ca95..e30ae41f93da 100644
--- a/typo3/sysext/core/Classes/Schema/TcaSchema.php
+++ b/typo3/sysext/core/Classes/Schema/TcaSchema.php
@@ -48,9 +48,15 @@ readonly class TcaSchema implements SchemaInterface
         return $this->name;
     }
 
-    public function getFields(): FieldCollection
+    public function getFields(...$fieldNames): FieldCollection
     {
-        return $this->fields;
+        if ($fieldNames === []) {
+            return $this->fields;
+        }
+
+        return new FieldCollection(
+            array_filter(iterator_to_array($this->fields), static fn(FieldTypeInterface $field): bool => in_array($field->getName(), $fieldNames, true))
+        );
     }
 
     public function hasField(string $fieldName): bool
diff --git a/typo3/sysext/styleguide/Configuration/TCA/tx_styleguide_elements_basic.php b/typo3/sysext/styleguide/Configuration/TCA/tx_styleguide_elements_basic.php
index 2d9d0404c212..30c145cb28a0 100644
--- a/typo3/sysext/styleguide/Configuration/TCA/tx_styleguide_elements_basic.php
+++ b/typo3/sysext/styleguide/Configuration/TCA/tx_styleguide_elements_basic.php
@@ -1550,6 +1550,18 @@ backend_layout {
                 'format' => 'datetime',
             ],
         ],
+        'none_4' => [
+            'label' => 'none_4',
+            'description' => 'format=date with format configuration',
+            'config' => [
+                'type' => 'none',
+                'format' => 'date',
+                'format.' => [
+                    'option' => '%d-%m',
+                    'strftime' => true,
+                ],
+            ],
+        ],
 
         'passthrough_1' => [
             'label' => 'passthrough_1',
@@ -1914,7 +1926,7 @@ backend_layout {
                 --div--;language,
                     language_1,
                 --div--;none,
-                    none_1, none_2, none_3,
+                    none_1, none_2, none_3, none_4,
                 --div--;passthrough,
                     passthrough_1, passthrough_2,
                 --div--;user,
diff --git a/typo3/sysext/styleguide/ext_tables.sql b/typo3/sysext/styleguide/ext_tables.sql
index f30916b63a49..6da4ac743fb9 100644
--- a/typo3/sysext/styleguide/ext_tables.sql
+++ b/typo3/sysext/styleguide/ext_tables.sql
@@ -33,6 +33,7 @@ CREATE TABLE tx_styleguide_elements_basic (
     none_1 text,
     none_2 text,
     none_3 text,
+    none_4 text,
 
     # type=passthrough needs manual configuration
     passthrough_1 text,
-- 
GitLab