Relations
Relations are resolved automatically from the TCA schema. By default, related records are serialized as plain IRI strings:
{
"color_id": "/_api/colors/1",
"categories": [
"/_api/categories/5",
"/_api/categories/8"
]
}
This format is compatible with API Platform Admin and other Hydra clients that resolve IRIs on demand. To inline the full related record instead, see Embedding related records below.
An IRI is only rendered when it dereferences, i.e. when the related table has a
registered resource with the show operation. A relation to any other table
renders the related record's uid instead ("color_). In a group
column that may reference several tables, where a bare uid would not say which
table it belongs to, such an item renders as {"@type": "…", "uid": 1}.
Controlling recursion depth
Control recursion depth with 'embed' => . The default depth
is 1.
'columns' => [
'color_id' => ['embed' => ['depth' => 2]],
],
Important
The related resource must be registered in the Api for embedding
to work.
Multi-table group fields
TCA type=group fields with multiple allowed tables store relations in the
prefixed tablename_ format (e.g. pages_). These fields
support embedding just like single-table relations:
// TCA definition
'related_items' => [
'label' => 'Related Items',
'config' => [
'type' => 'group',
'allowed' => 'tx_myext_domain_model_article,tx_myext_domain_model_color',
],
],
// API resource configuration
'columns' => [
'related_items' => ['groups' => ['list', 'show'], 'embed' => true],
],
The embedded response contains full objects from different tables, each with its
own @type and fields:
{
"related_items": [
{
"@id": "/_api/articles/201",
"@type": "Article",
"uid": 201,
"title": "Related Article"
},
{
"@id": "/_api/colors/1",
"@type": "Color",
"uid": 1,
"name": "Red"
}
]
}
Each referenced table is resolved to its registered API resource. When no
resource is registered for a table, a default-mode config is synthesized
automatically (all TCA columns exposed). Its @type is derived from the
table name (tx_ → Tx) unless the
column sets resource.
An embedded record that has no item endpoint — from an unregistered table, or
from a resource without the show operation — carries no @id. It is a
JSON-LD blank node: an @id would point at a URL that answers 404.
Bulk preloading is fully supported: collection requests issue one SELECT
per referenced table regardless of how many parent rows exist, avoiding N+1
queries.
Writing nested objects
When a create or update request contains a related record as an object
(rather than a UID), TCA_API creates the child record in-line. The child
table must have a registered resource in the API registry; a nested object for
an unregistered table is rejected with 422 and an UNRESOLVABLE_
violation naming the column and the table.
Passing an existing UID or IRI never needs a registered child resource, so linking is unaffected.
Nested writes without a child resource
A child table that should not be reachable as a resource of its own does not
need one. Declare nested on the parent column instead — it states the
role required to create through that relation, and the child's shape is derived
from TCA:
use MaikSchneider\TcaApi\Enum\AccessRole;
'columns' => [
'note_id' => [
'groups' => ['list', 'show', 'create'],
'nestedWrite' => AccessRole::FE_USER,
],
],
nested accepts the same shapes as a security entry: an
Access, an [Access tuple, or a
[class- callable.
When the child table is registered, nested still wins for the access
check — who may write through a relation is a narrower question than what the
child resource permits on its own endpoint — while columns, validation and
ownership continue to come from the registered resource.
{
"title": "My Article",
"color_id": { "name": "Red" }
}
By default the child resource is looked up by the foreign table name from
TCA. When multiple resources are registered for the same DB table, use the
resource column option to pin the lookup to a specific resource:
'columns' => [
'color_id' => [
'groups' => ['list', 'show', 'create', 'update'],
'resourceName' => 'colors', // look up "colors" resource, not by table
],
],
This also controls which resource's security. role is enforced on the
nested write and which resource's column config is used for validation.
Note
If resource is set but no matching resource is registered, TCA_API
throws an \Invalid at load time (validated in
Api).
Supported relation types
| TCA type | Default output | Embedding |
|---|---|---|
select / group | UID list (1,2,3) — single table → IRI strings | Yes |
select / group | Prefixed list (table_) — multi-table → IRI strings | Yes |
inline / select | foreign_ back-reference → IRI strings | Yes |
Any + MM | Intermediate MM table → IRI strings | Yes |
type=group + MM | Column holds count, relations in MM → IRI strings | Yes |