---
title: "Adding a record kind"
manual: "TYPO3 EXT:thuecat"
version: "main"
permalink: "https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-record-kind@main"
source: "Developers/Import/AddingARecordKind.rst"
rendered: "2026-10-09T11:47:03+00:00"
---

# Adding a record kind {#developers-import-record-kind}

A record kind is one upstream `@type` imported into one table. This page walks through what a new
one needs, using the smallest existing kind, the town, as the example.

**On this page**

-   [The town as an example](https://docs.typo3.org/permalink/werkraummedia/thuecat:the-town-as-an-example@main)
-   [The table](https://docs.typo3.org/permalink/werkraummedia/thuecat:the-table@main)
-   [The entity](https://docs.typo3.org/permalink/werkraummedia/thuecat:the-entity@main)
-   [Can the kind be a root?](https://docs.typo3.org/permalink/werkraummedia/thuecat:can-the-kind-be-a-root@main)
-   [Relations to and from the kind](https://docs.typo3.org/permalink/werkraummedia/thuecat:relations-to-and-from-the-kind@main)
-   [Checklist](https://docs.typo3.org/permalink/werkraummedia/thuecat:checklist@main)

## The town as an example {#the-town-as-an-example}

`\WerkraumMedia\ThueCat\Import\Parser\Entity\TownEntity` writes into `tx_thuecat_town`. It

-   claims nodes whose `@type` contains `schema:City`;
-   reads `schema:name` and `schema:description` once for the default language and once per
    translation language;
-   builds its address as an inline child record;
-   records `thuecat:managedBy` as a reference for the resolver to turn into a relation.

Everything else a town needs, the import does on its own: finding the existing row, writing
translations, logging the saved record.

## The table {#the-table}

The table is an ordinary TYPO3 table with TCA. It needs:

-   a `remote_id` column, see [Records, identity and languages](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-records@main);
-   the usual language columns, if the kind is translatable — the translations are created with the
    DataHandler's `localize` command and need them;
-   a column per imported value, named as the entity's property.

Image fields are FAL fields; set `allowLanguageSynchronization` on them so translations keep
following the default language.

## The entity {#the-entity}

An entity extends `\WerkraumMedia\ThueCat\Import\Parser\Entity\AbstractEntity`, or
`AbstractPlaceEntity` for a kind that has an address.

-   **Which nodes it parses**

    `handlesTypes()` lists the `@type` values it claims. A node usually carries several types
    — a tourist information is also a place, an organisation is also a thing. When several entities
    claim the same node, the highest `getPriority()` wins. The default is 10; the more specific
    kinds use 20 or 30. Check which existing entities claim the same types before choosing one.

-   **Where it writes**

    The `TABLE` constant names the table. Each property becomes a column of the same name, so
    declare every property with its default value.

-   **What it reads**

    `parse()` receives the node, the default language and the translation languages. It fills
    the properties for the default language and records each translated value with
    `recordTranslation()`. Empty values are dropped; see [Records, identity and languages](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-records@main) for
    what that means for values upstream clears.

-   **What it cannot resolve itself**

    Relations to other upstream objects are recorded as references with `recordTransient()`,
    media with `recordMediaTransient()` and keywords with `recordKeywords()`. See
    [Relations](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-relations@main).

-   **What it builds along the way**

    Rows built from nested data — addresses, opening hours, dates — are separate entities returned by
    `getChildren()`. Such an entity claims no type of its own, so the parser never picks it for
    a node; only its parent creates it.

Registration is automatic: every class implementing
`\WerkraumMedia\ThueCat\Import\Parser\Entity\EntityInterface` carries the `import.entity` tag
through the interface, and the parser receives all of them.

## Can the kind be a root? {#can-the-kind-be-a-root}

A kind is either imported on its own, as a root of an import configuration, or only reached as a
relation of something else. Towns are only reached as relations; attractions, events and trails are
roots.

A kind that can be a root implements
`\WerkraumMedia\ThueCat\Import\Parser\Entity\TopLevelEntityInterface` and names its **anchor
scope**: the name its category and keyword settings are read under, for example `trails` for
`tx_thuecat_trail`. The scope is declared, not derived from the class name, because integrators
already configured the names that exist.

A new scope needs its settings, one pair per tree the kind fills: `import.<scope>.category.*`
if it has categories, `import.<scope>.keywords.*` if it has keywords. They go into the site
set definition, grouped under a category of their own, and as `import<Scope>…` keys into
[`ext_conf_template.txt`](https://docs.typo3.org/m/typo3/reference-coreapi/14.3/en-us/ExtensionArchitecture/FileStructure/ExtConfTemplate.html#file-extension-ext-conf-template-txt). Trails, for example, have keywords only and therefore two settings.
Until the settings exist, the kind falls back to the `thuecat` scope. See
[Where a tree lives: anchors and scopes](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-anchors@main).

A kind reached only as a relation implements nothing extra and uses the `thuecat` scope.

## Relations to and from the kind {#relations-to-and-from-the-kind}

Other kinds point at the new one only where the resolver knows the target table and the field to
write. That is `Resolver::BUCKET_MAP`, explained in [Relations](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-relations@main). Adding
a target table there also means adding its field to the TCA of every owner table.

In the backend, relation fields pointing at the new table should only offer records of the current
site. Check whether the table belongs in the site-scoped selects, see
[Adding a record kind](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-backend-relation-scoping-new-record-kind@main). Leaving it out does not fail; the field
silently offers records of every site.

## Checklist {#checklist}

1.  Table with `remote_id`, language columns and TCA.
1.  Entity with `TABLE`, typed properties with defaults, `handlesTypes()` and, if types
    overlap, a priority.
1.  `TopLevelEntityInterface` and the scope's settings, if the kind can be a root.
1.  `BUCKET_MAP` entries and owner fields, if other kinds relate to it.
1.  Site-scoped selects in the backend.
1.  A functional test importing one fixture of the kind, and one re-importing it, see
    [Testing an import](https://docs.typo3.org/permalink/werkraummedia/thuecat:developers-import-testing@main).
