Columns
The columns section of a resource definition controls which database columns
are exposed and how they behave. Each entry maps to a database column name.
Visibility modes
TCA_API has two visibility modes. The mode is auto-detected per resource:
- Default mode
- Active when no column has
groupsset. All non-system TCA columns (i.e. nothidden,deleted,tstamp,crdate, language/workspace fields) are automatically exposed for read and write. - Explicit mode
- Active as soon as any column declares
groups. Only columns with a matchinggroupsentry are exposed; all others are hidden.
Serialization groups
Use groups to control visibility per operation:
'columns' => [
'title' => ['groups' => ['list', 'show', 'create', 'update']], // everywhere
'teaser' => ['groups' => ['list']], // list only
'body' => ['groups' => ['show']], // detail view only
'secret' => ['groups' => []], // never exposed
],
Valid group names: list, show, create, update.
Column options reference
All keys are optional.
| Key | Description |
|---|---|
type | Data type hint for OpenAPI schema (e.g. string, integer). |
readable | true — include in responses. Legacy option; use groups instead. |
writable | true — accept in create/update requests. Legacy option; use groups
instead. |
groups | Array of operations where this column is active — triggers explicit mode
(list, show, create, update). |
required | Require on POST/PUT (skipped on PATCH if absent). |
embed | true or ['depth' => N] — inline related records instead of shallow
stubs. See Relations. |
resourceName | Override the related resource used for relation columns. Normally TCA
API looks up the child resource by its DB foreign_table. Set this
when multiple resources are registered for the same table, or to
explicitly control which resource's security and column config applies
to nested writes. See Relations for a full example. |
processor | Column processor class. Does not trigger explicit mode. See Column processors. |
callback | [ClassName::class, 'method'] tuple invoked after all columns and
relations are resolved (but before virtual properties). Its return
value replaces the column's value. See Column callbacks. |
validators | Array of validation rules. See Validation. |
upload | Enable file upload for this column via multipart/form-data requests.
Must be an array with at least a folder key (FAL storage reference,
e.g. 1:/uploads/). See File Uploads for all options. |
image | Image processing options for ImageProcessor columns. Controls
dimensions, crop variant selection, format conversion, and URL mode.
See Column processors for all options. |
route | URL generation options for RouteEnhancerProcessor columns and
virtual properties. Drives the TYPO3 site router so any
routeEnhancer configured on the target page applies transparently.
See Route Enhancer for all options. |
Field type support
The serializer automatically handles all TYPO3 TCA field types. Relational types are resolved via dedicated serializers; scalar types that store encoded data are decoded before output; sensitive types are excluded entirely.
| TCA type | Handling | Output |
|---|---|---|
file | FileFieldSerializer — auto-selects ImageProcessor or
FileProcessor | Processed file/image object(s) |
category | RelationSerializer | Shallow stub or embedded record |
select (relational) | RelationSerializer | Shallow stub or embedded record |
inline | RelationSerializer | Shallow stub or embedded record |
group | GroupFieldSerializer | Shallow stub or embedded record |
json | Auto-decoded via json_decode | Decoded array/object |
imageManipulation | Auto-decoded via json_decode | Decoded crop config object |
flex | Auto-decoded via GeneralUtility::xml2array | Decoded associative array |
datetime | Auto-formatted to ISO 8601 (DateTimeInterface::ATOM) | "2024-01-01T00:00:00+00:00" or null |
link | Auto-applies TypoLinkProcessor | Resolved public URL string |
password | Excluded — never appears in API responses | (column omitted) |
input, text, number, email, color, country,
slug, radio, select (static) | Raw DB value | String, integer, or appropriate scalar |
check | Raw DB value | Bitmask integer |
language | Excluded by TcaColumnDiscovery via ctrl.languageField | (column omitted) |
folder, none, passthrough, user | Raw DB value | Implementation-defined |
An explicit processor on a column definition always overrides the automatic
handling described above.
Column processors
Column processors transform values during serialization. The extension ships two built-in processors:
FileProcessor-
Serialises file references (FAL). Useful for file download columns.
use MaikSchneider\TcaApi\Serializer\FileProcessing\FileProcessor; 'downloads' => [ 'groups' => ['list', 'show'], 'processor' => FileProcessor::class, ],Copied! TypoLinkProcessor-
Resolves TYPO3 typolinks to full URLs.
use MaikSchneider\TcaApi\Serializer\Processing\TypoLinkProcessor; 'article_url' => [ 'groups' => ['list', 'show'], 'processor' => TypoLinkProcessor::class, ],Copied! RouteEnhancerProcessor-
Generates a frontend URL per record from a typed
routeconfig and defers URL construction to the TYPO3 site router, so any configuredrouteEnhancer(e.g. an Extbase News plugin) applies transparently. Most often used on a virtual property:use MaikSchneider\TcaApi\Serializer\Processing\RouteEnhancerProcessor; 'virtualProperties' => [ 'url' => [ 'processor' => RouteEnhancerProcessor::class, 'route' => [ 'pid' => '{$tca_api.news.detailPid}', 'extension' => 'News', 'plugin' => 'Pi1', 'controller' => 'News', 'action' => 'detail', 'arguments' => ['news' => '{uid}'], ], ], ],Copied!See Route Enhancer for placeholder grammar, language handling, and the full options reference.
ImageProcessor-
Serialises image file references (FAL) with full crop-variant support and configurable processing options. By default the processor is used for every
type=fileTCA column that has no explicitprocessorkey.Options are controlled via the
imagesub-key on the column definition:use MaikSchneider\TcaApi\Serializer\FileProcessing\ImageProcessor; 'hero_image' => [ 'groups' => ['list', 'show'], 'processor' => ImageProcessor::class, // optional — also the default for type=file 'image' => [ 'maxWidth' => 1200, 'maxHeight' => 800, 'fileExtension' => 'webp', // omit cropVariant → all variants returned as a map ], ],Copied!Output mode 1 — all variants (
cropVariantomitted ornull):Every crop variant stored on the file reference is processed and returned as a
cropVariantsmap:{ "hero_image": { "publicUrl": "/fileadmin/hero.jpg", "mimeType": "image/jpeg", "fileSize": 204800, "metadata": { "title": "Hero", "alternative": "A hero image", "description": null, "copyright": "© 2026" }, "cropVariants": { "default": { "publicUrl": "/fileadmin/_processed_/hero_c.webp", "width": 1024, "height": 512 }, "mobile": { "publicUrl": "/fileadmin/_processed_/hero_m.webp", "width": 375, "height": 200 } } } }Copied!Output mode 2 — single variant (
cropVariantset to a variant ID):Only that variant is processed and the result is inlined as the top-level image — no
cropVariantskey:'hero_image' => [ 'groups' => ['list', 'show'], 'image' => [ 'maxWidth' => 1200, 'cropVariant' => 'default', // single-variant mode ], ],Copied!{ "hero_image": { "publicUrl": "/fileadmin/_processed_/hero_c.webp", "width": 1200, "height": 600, "mimeType": "image/jpeg", "fileSize": 204800, "metadata": { "title": "Hero", "alternative": null, "description": null, "copyright": null } } }Copied!Key Type Description widthstringTarget width. Accepts a plain integer ( "400"), crop-scale ("400c"), or scale-down-only ("400m"). Mutually usable withmaxWidth.heightstringTarget height — same notation as width.minWidthintMinimum width in pixels. Must be a positive integer. minHeightintMinimum height in pixels. Must be a positive integer. maxWidthintMaximum width in pixels. Must be a positive integer. maxHeightintMaximum height in pixels. Must be a positive integer. cropVariantstringCrop variant identifier (e.g. 'default','mobile'). When set, only that variant is processed and the URL is returned as top-levelpublicUrl(nocropVariantskey). When omitted, all variants are returned as acropVariantsmap.fileExtensionstringTarget file extension for format conversion, e.g. 'webp'.absoluteboolForce an absolute URL. Default: false.
Custom processors must implement
\Maik.
Column callbacks
A callback is a lighter-weight alternative to a processor when you only need
to post-process a single column. Unlike a processor — which receives just the
raw column value — a callback runs last, after every column, relation, and
relation has already been resolved into the response. It receives the
fully serialized row and the raw DB row, and its return value replaces the
column's value. Column callbacks run before virtual properties, so a virtual
property can build on the final, callback-transformed column values.
use Vendor\MyExt\Api\ArticleCallbacks;
'columns' => [
'title' => ['groups' => ['list', 'show']],
'color_id' => ['groups' => ['list', 'show'], 'embed' => true],
// Derive a label from the already-embedded relation:
'label' => [
'groups' => ['list', 'show'],
'callback' => [ArticleCallbacks::class, 'buildLabel'],
],
],
The callback signature is (array $serializedRow, array $rawRow): mixed:
final class ArticleCallbacks
{
public function buildLabel(array $serializedRow, array $rawRow): string
{
// $serializedRow already contains the resolved 'color_id' relation.
$color = $serializedRow['color_id']['name'] ?? 'n/a';
return sprintf('%s (%s)', $serializedRow['title'] ?? '', $color);
}
}
The callback class is instantiated via
General, so
constructor dependency injection works. Because callbacks run after relation
resolving, they can read embedded relations, processor output, and other columns
from $serializedRow. They run before virtual properties, so a virtual
property may consume a column's callback result. Callbacks honour the same
visibility rules as the column itself — they are skipped when the column is
hidden by groups for the current operation or excluded by a sparse fieldset
(?fields[]=…).
The same callback key is also available on virtual properties (see
Virtual Properties), where it is the primary mechanism for computing a
value that has no backing column.