---
title: "Extending the vocabulary"
manual: "schema"
version: "main"
permalink: "https://docs.typo3.org/permalink/brotkrueml/schema:extending-vocabulary@main"
source: "Developer/ExtendingVocabulary.rst"
rendered: "2026-09-25T08:33:13+00:00"
---

# Extending the vocabulary {#extending-vocabulary}

Target group: **Developers**

**Table of Contents**

-   [Introduction](https://docs.typo3.org/permalink/brotkrueml/schema:introduction@main)
-   [Register additional properties](https://docs.typo3.org/permalink/brotkrueml/schema:register-additional-properties@main)
-   [Adding types](https://docs.typo3.org/permalink/brotkrueml/schema:adding-types@main)
-   [Add a new WebPage type](https://docs.typo3.org/permalink/brotkrueml/schema:add-a-new-webpage-type@main)
-   [Add a new enumeration](https://docs.typo3.org/permalink/brotkrueml/schema:add-a-new-enumeration@main)

## Introduction {#introduction}

The TYPO3 schema extension ships [type models](https://docs.typo3.org/permalink/brotkrueml/schema:api@main) and [view helpers](https://docs.typo3.org/permalink/brotkrueml/schema:view-helpers@main) with their properties from the core section of the schema.org
definitions. However, there are several extensions, like
[Health and lifesciences](https://schema.org/docs/meddocs.html) or [Autos](https://schema.org/docs/automotive.html). There are also [pending types and properties](https://pending.schema.org/docs/pending.home.html) available that enable schema.org to introduce terms on an
experimental basis.

The embedding of these vocabulary extensions goes beyond the scope of this TYPO3
extension, as it will considerably increase the number of terms – while most of
them are not used by the majority of users.

For your convenience there are separate extensions to [extend the
vocabulary](https://docs.typo3.org/permalink/brotkrueml/schema:vocabulary@main) available with the terms of the different sections. But
if you only want to add only a few terms you are encouraged to add them on your
own, especially pending terms.

Pending terms are experimental, after a certain time a term will be incorporated
into the core section or dropped. This depends on the usage, adoption and
discussions. To maintain backward-compatibility and to not break any pages,
pending types are also not supplied by this extension.

But the vocabulary delivered with this extension can be extended according to
your needs. This chapter describes the necessary steps to register additional
properties to existing types or to introduce new types on your website.

## Register additional properties {#extending-register-additional-properties}

Sometimes it may be necessary to use properties that are not standardised or
[pending](https://pending.schema.org/), or to add [property annotations](https://schema.org/docs/actions.html#part-4). Therefore the schema extension
provides a way to extend types.

These additional properties are not only available in the [API](https://docs.typo3.org/permalink/brotkrueml/schema:api@main) but
also as arguments in the [view helpers](https://docs.typo3.org/permalink/brotkrueml/schema:view-helpers@main).

To add one or more properties to a type, create a new class, for example in
`EXT:my_extension/Classes/Schema/AdditionalProperties/` and implement
the `BrotkruemlSchemaCoreAdditionalPropertiesInterface`. The interface
requires two methods:

-   **getType(): string**

    Returns the type name.

-   **getAdditionalProperties(): array**

    Return a list of additional properties as string.

Here is an example for such an implementation:

**EXT:my_extension/Classes/Schema/AdditionalProperties/Car.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Schema\AdditionalProperties;

use Brotkrueml\Schema\Core\AdditionalPropertiesInterface;

final class Car implements AdditionalPropertiesInterface
{
    public function getType(): string
    {
        return 'Car';
    }

    public function getAdditionalProperties(): array
    {
        return [
            'accelerationTime',
            'emissionsCO2',
            'fuelCapacity',
            'seatingCapacity',
            'speed',
        ];
    }
}

```

For each type create a new PHP class.

> [!IMPORTANT]
> Flush the cache after a change via the console or
> **Admin Tools > Maintenance**.

> [!NOTE]
> About 1-2 times a year a new version of the schema.org definition is
> [released](https://schema.org/docs/releases.html). The extension adopts these changes in future releases. If you
> register a pending property for a type, this property can be included in the
> core section in a later version of this extension. However, it doesn't do
> any harm to register a property that already exists.

## Adding types {#extending-adding-types}

You can add additional types for use in the [API](https://docs.typo3.org/permalink/brotkrueml/schema:api@main) or as a
[WebPage type](https://docs.typo3.org/permalink/brotkrueml/schema:webpage-types@main). As an example, in March 2020, schema.org
introduces a new [VirtualLocation](https://schema.org/VirtualLocation) type related to the corona crisis, which
was quickly adopted by Google. The type can be used as [location](https://schema.org/location) in the
[Event](https://schema.org/Event) type. So let's start with this example.

1.  Create the type model class

    The model class for a type defines the available properties. The model class
    for the `VirtualLocation` type may look like the following:

    **EXT:my_extension/Classes/Schema/Type/VirtualLocation.php**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\Schema\Type;

    use Brotkrueml\Schema\Attributes\Type;
    use Brotkrueml\Schema\Core\Model\AbstractType;

    #[Type('VirtualLocation')]
    final class VirtualLocation extends AbstractType
    {
        protected static array $propertyNames = [
            'additionalType',
            'alternateName',
            'description',
            'disambiguatingDescription',
            'identifier',
            'image',
            'mainEntityOfPage',
            'name',
            'potentialAction',
            'sameAs',
            'subjectOf',
            'url',
        ];
    }

    ```

    In the example, the class is stored in `Classes/Schema/Type` of your
    extension, but you can choose any namespace. It has to extend from
    `\Brotkrueml\Schema\Core\Model\AbstractType`. The class must have the
    `\Brotkrueml\Schema\Attributes\Type` attribute assigned. It has one
    mandatory parameter: the type name. The protected static property
    `$propertyNames` contains the available schema.org properties.

    Now you can use the `VirtualLocation` in your PHP code:

    **EXT:my_extension/Classes/Controller/MyController.php**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\Controller;

    use Brotkrueml\Schema\Manager\SchemaManager;
    use Brotkrueml\Schema\Type\TypeFactory;

    final class MyController
    {
        public function __construct(
            private readonly SchemaManager $schemaManager,
            private readonly TypeFactory $typeFactory,
        ) {}

        public function createVirtualLocation(): void
        {
            // ...

            $location = $this->typeFactory->create('VirtualLocation');
            $location->setProperty('url', 'https://example.com/my-webinar-12345/register');
            $this->schemaManager->addType($location);

            // ...
        }
    }

    ```

    > [!NOTE]
    > With the `\Brotkrueml\Schema\Attributes\Type` attribute the class
    > is recognised as a type model class. All types are collected on
    > DI compile time: When you add/change/remove
    > a class, you have to flush the cache via
    > **Admin Tools > Maintenance** or the `flush:cache` command
    > on [CLI](https://docs.typo3.org/m/typo3/reference-coreapi/14.3/en-us/ApiOverview/CommandControllers/Index.html#symfony-console-commands).
1.  Create the view helper (optional)

    If you have the need for a view helper with that type, you can create one:

    **EXT:my_extension/Classes/ViewHelpers/Schema/Type/VirtualLocationViewHelper.php**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\ViewHelpers\Schema\Type;

    use Brotkrueml\Schema\Core\ViewHelpers\AbstractTypeViewHelper;

    final class VirtualLocationViewHelper extends AbstractTypeViewHelper
    {
        protected string $type = 'VirtualLocation';
    }

    ```

    > [!NOTE]
    > The name of the type must be defined with the `$type` property.

    To use the `schema` namespace in Fluid templates also with your custom
    view helpers add the following snippet to the `ext_localconf.php` file
    of your extension:

    **EXT:my_extension/ext_localconf.php**

    ```php
    $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['schema'][]
       = 'MyVendor\\MyExtension\\ViewHelpers\\Schema';
    ```

    This way you don't have to think about which namespace to use. And if the
    pending type is moved to the core section you have no need to touch your
    Fluid templates. Of course, feel free to use another namespace.

> [!WARNING]
> **Attention**
>
> Add the schema extension as a dependency to your extension. This ensures that
> your class models take precedence over the delivered models from the schema
> extension. This may be necessary, if you define a pending type with pending
> properties (which you also use) to avoid breaks when the type is included
> into the core section but some properties aren't.

> [!TIP]
> Have a look into the [schema_virtuallocation](https://github.com/brotkrueml/schema-virtuallocation) extension. It serves as a
> blueprint for adding own types and view helpers.

## Add a new WebPage type {#add-a-new-webpage-type}

If you are responsible for a medical website, the chances are high that you need
the [MedicalWebPage](https://schema.org/MedicalWebPage) web page type, which is part of the Health schema.org
extension.

Register this web page type so that it is available in the [web page type
list](https://docs.typo3.org/permalink/brotkrueml/schema:webpage-types-list@main) in the page properties. Simply follow the steps in
the [Adding types](https://docs.typo3.org/permalink/brotkrueml/schema:extending-adding-types@main) section.

Mark your class as a WebPage type with the interface
`\Brotkrueml\Schema\Core\Model\WebPageTypeInterface`:

**EXT:my_extension/Classes/Schema/Type/MedicalWebPage.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Schema\Type;

use Brotkrueml\Schema\Attributes\Type;
use Brotkrueml\Schema\Core\Model\AbstractType;
use Brotkrueml\Schema\Core\Model\WebPageTypeInterface;

#[Type('MedicalWebPage')]
final class MedicalWebPage extends AbstractType implements WebPageTypeInterface
{
    protected static array $propertyNames = [
        // ... the properties ...
    ];
}

```

The new web page type can now be selected in the page properties:

![MedicalWebPage in the list of available web page types](../Images/Developer/MedicalWebPageTypeSelection.png)

## Add a new enumeration {#extending-adding-enumeration}

schema.org provides the ability to use enumerations as values for certain
properties. The TYPO3 schema extensions provide the
[enumerations](https://docs.typo3.org/permalink/brotkrueml/schema:enumerations@main) defined by schema.org. However, there are
some enumerations where schema.org refers to other vocabularies. These
enumerations are not provided by the TYPO3 schema extensions.
An example is [BusinessEntityType](https://schema.org/BusinessEntityType), which suggests using the [GoodRelations](http://www.heppnetz.de/ontologies/goodrelations/v1)
terms. If you want to use them, you can define and use your own enumeration to
provide a defined set of of possible values satisfiying your needs (instead of
plain strings):

**EXT:my_extension/Classes/Schema/Enumeration/BusinessEntityType.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Schema\Enumeration;

use Brotkrueml\Schema\Core\Model\EnumerationInterface;

enum BusinessEntityType implements EnumerationInterface
{
    case Business;
    case Enduser;
    case PublicInstitution;
    case Reseller;

    public function canonical(): string
    {
        return 'http://purl.org/goodrelations/v1#' . $this->name;
    }
}

```

All enumeration types must implement the interface
[Brotkrueml\\Schema\\Core\\Model\\EnumerationInterface](https://docs.typo3.org/permalink/brotkrueml/schema:api-enumeration-interface@main),
which requires a `canonical()` method. Depending on the case, it returns
the string to use in the JSON-LD output.

Now you can make use of this enum in PHP, for example:

```php
// use MyVendor\MyExtension\Schema\Enumeration\BusinessEntityType;

$demand = $this->typeFactory->create('Demand');
$demand->setProperty('eligibleCustomerType', BusinessEntityType::Enduser);
```

or in [Fluid](https://docs.typo3.org/permalink/brotkrueml/schema:enumerations-view-helper@main):

```html
<schema:type.demand
   eligibleCustomerType="{f:constant(
      name: \MyVendor\MyExtension\Schema\Enumeration\BusinessEntityType::Enduser
   )}"
/>
```

which results in:

```json
{
   "@type": "Demand",
   "eligibleCustomerType": "http://purl.org/goodrelations/v1#Enduser"
}
```

Another use case might be to create your own extended `GenderType`
enum instead using the enum from this extension:

**EXT:my_extension/Classes/Schema/Enumeration/GenderType.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Schema\Enumeration;

use Brotkrueml\Schema\Core\Model\EnumerationInterface;

enum GenderType implements EnumerationInterface
{
    case Androgyne;
    case Bigender;
    case Binary;
    case Cis;
    case DemiBoy;
    case DemiGirl;
    case Eunuch;
    case Female;
    case Genderless;
    case Intersex;
    case Male;
    case Multigender;
    case Neither;
    case Polygender;
    case Queer;
    case Transgender;

    public function canonical(): string
    {
        return match ($this) {
            self::DemiBoy => 'Demi-boy',
            self::DemiGirl => 'Demi-girl',
            self::Female, self::Male => 'https://schema.org/' . $this->name,
            default => $this->name,
        };
    }
}

```
