---
title: "Using The API"
manual: "schema"
version: "main"
permalink: "https://docs.typo3.org/permalink/brotkrueml/schema:api@main"
source: "Developer/Api.rst"
rendered: "2026-09-18T07:29:46+00:00"
---

# Using The API {#using-the-api}

Target group: **Developers**

**Table of Contents**

-   [Introduction](https://docs.typo3.org/permalink/brotkrueml/schema:introduction@main)
-   [Starting with examples](https://docs.typo3.org/permalink/brotkrueml/schema:starting-with-examples@main)
-   [The model in-depth](https://docs.typo3.org/permalink/brotkrueml/schema:the-model-in-depth@main)
-   [Schema manager](https://docs.typo3.org/permalink/brotkrueml/schema:schema-manager@main)
-   [Node identifier](https://docs.typo3.org/permalink/brotkrueml/schema:node-identifier-1@main)
-   [Blank node identifier](https://docs.typo3.org/permalink/brotkrueml/schema:blank-node-identifier-1@main)
-   [Other useful APIs](https://docs.typo3.org/permalink/brotkrueml/schema:other-useful-apis@main)

## Introduction {#introduction}

With the extension's API you can define the structured markup with PHP. For
example, create a class which gets an Extbase model as input and defines the
markup. Then instantiate the class in an action of your controller.

Each type model class in the PHP namespace `\Brotkrueml\Schema\Model\Type`
inherits from the abstract class
`\Brotkrueml\Schema\Core\Model\AbstractType` which defines methods to set
and get the properties of a model.

There are currently over 600 models available.

## Starting with examples {#starting-with-examples}

### Types {#types-1}

Let's start with a simple example. Imagine you describe a [person](https://schema.org/Person) on a
plugin's detail page that you want to enrich with structured markup. First you
have to create the schema model:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Type\TypeFactory;

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

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

        $person = $this->typeFactory->create('Person');

        // ...
    }
}

```

The schema type `Person` maps to the model
`\Brotkrueml\Schema\Model\Type\Person`. You can use every accepted type
from the core section of [schema.org](https://schema.org/docs/full.html). Also have a look into the
`Classes\Model\Type` folder of this extension to get a general idea
of the available types.

If the type is not available a
`\Brotkrueml\Schema\Type\ModelClassNotFoundException` is thrown.

Every type implements the `\Brotkrueml\Schema\Core\Model\TypeInterface`.
You will find a list of the available methods in the section
[Available type model methods](https://docs.typo3.org/permalink/brotkrueml/schema:api-type-interface@main).

Surely you will need to add some properties:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Model\Enumeration\GenderType;
use Brotkrueml\Schema\Type\TypeFactory;

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

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

        $person = $this->typeFactory->create('Person');
        $person
            ->setId('https://example.org/#person-42')
            ->setProperty('givenName', 'John')
            ->setProperty('familyName', 'Smith')
            ->setProperty('gender', GenderType::Male);

        // ...
    }
}

```

That was easy ...

Let's go on and define an event the person attends:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Type\TypeFactory;

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

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

        $event = $this->typeFactory->create('Event')
            ->setProperty('name', 'Fancy Event')
            ->setProperty('image', 'https:/example.org/event.png')
            ->setProperty('url', 'https://example.org/')
            ->setProperty('isAccessibleForFree', true)
            ->setProperty('sameAs', 'https://mastodon.social/@fancy-event')
            ->addProperty('sameAs', 'https://pixelfed.social/@fancy-event')
        ;

        // ...
    }
}

```

Now we have to connect the two types together:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Type\TypeFactory;

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

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

        $person = $this->typeFactory->create('Person');
        $event = $this->typeFactory->create('Event');

        // ...

        $person->setProperty('performerIn', $event);

        // ...
    }
}

```

The defined models are ready to embed on the web page. The schema manager does
that for you:

**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 doSomething(): void
    {
        // ...

        $person = $this->typeFactory->create('Person');

        // ...

        $this->schemaManager->addType($person);

        // ...
    }
}

```

That's it ... if you call the according page the structured markup is embedded
automatically into the head section:

```json
{
   "@context": "https://schema.org/",
   "@type": "Person",
   "@id": "https://example.org/#person-42",
   "givenName": "John",
   "familyName": "Smith",
   "gender": "https://schema.org/Male",
   "performerIn": {
      "@type": "Event",
      "name": "Fancy Event",
      "image": "https://example.org/event.png",
      "url": "https://example.org",
      "isAccessibleForFree": "https://schema.org/True",
      "sameAs": ["https://mastodon.social/@fancy-event", "https://pixelfed.social/@fancy-event"]
   }
}
```

### Multiple types {#multiple-types-1}

JSON-LD allows multiple types for a node. The rendered `@type` property is then
an array, the properties of the single types are merged. This way, a node can
be, for example, a `Product` and a `Service` at the same time - which can be
useful in some cases.

The technical difference to a single type is only that you call
`\Brotkrueml\Schema\Type\TypeFactory->create()` with more than one
argument:

**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 doSomething(): void
    {
        // ...

        $productAndService = $this->typeFactory->create('Product', 'Service');
        $productAndService
            ->setId('https://example.org/#my-product-and-service')
            ->setProperty('name', 'My product and service')
            ->setProperty('manufacturer', 'Acme Ltd.') // from Product
            ->setProperty('provider', 'Acme Ltd.') // from Service
        ;
        $this->schemaManager->addType($productAndService);

        // ...
    }
}

```

The factory method returns an instance of the
`\Brotkrueml\Schema\Core\Model\MultipleType` class which provides the
same API as a single type.

This results in the following JSON-LD:

```json
{
   "@context": "https://schema.org/",
   "@type": ["Product", "Service"],
   "@id": "https://example.org/#my-product-and-service",
   "manufacturer": "Acme Ltd.",
   "name": "My product and service",
   "provider": "Acme Ltd."
}
```

### Node identifiers {#node-identifiers}

JSON-LD supports the usage of `@id` as reference without giving a type. This is
useful when using circular references, for example:

```json
{
   "@context": "https://schema.org/",
   "@type": "Person",
   "@id": "https://example.org/#john-smith",
   "name": "John Smith",
   "knows": {
      "@type": "Person",
      "name": "Sarah Jane Smith",
      "knows": { "@id": "https://example.org/#john-smith" }
   }
}
```

You can accomplish this with the help of the
`\Brotkrueml\Schema\Core\Model\NodeIdentifier` class:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Core\Model\NodeIdentifier;
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 doSomething(): void
    {
        // ...

        $johnSmithId = new NodeIdentifier('https://example.org/#john-smith');

        $person1 = $this->typeFactory->create('Person');
        $person1->setId($johnSmithId);
        $person1->setProperty('name', 'John Smith');

        $person2 = $this->typeFactory->create('Person');
        $person2->setProperty('name', 'Sarah Jane Smith');
        $person2->setProperty('knows', $johnSmithId);

        $person1->setProperty('knows', $person2);

        $this->schemaManager->addType($person1, $person2);

        // ...
    }
}

```

As you can see in the example, you can also use a node identifier as an
argument for `->setId()` instead of a string.

### Blank node identifiers {#blank-node-identifiers}

Sometimes it is not necessary (or possible) to define a globally unique ID
with an IRI. For these cases you can use a [blank node identifier](https://www.w3.org/TR/json-ld11/#example-95-referencing-an-unidentified-node).

The above example can also be used with a blank node identifier:

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

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Core\Model\BlankNodeIdentifier;
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 doSomething(): void
    {
        // ...

        $johnSmithId = new BlankNodeIdentifier();

        $person1 = $this->typeFactory->create('Person');
        $person1->setId($johnSmithId);
        $person1->setProperty('name', 'John Smith');

        $person2 = $this->typeFactory->create('Person');
        $person2->setProperty('name', 'Sarah Jane Smith');
        $person2->setProperty('knows', $johnSmithId);

        $person1->setProperty('knows', $person2);

        $this->schemaManager->addType($person1, $person2);

        // ...
    }
}

```

To use a blank node identifier instantiate the class
`\Brotkrueml\Schema\Core\Model\BlankNodeIdentifier`. The identifier is
generated automatically on instantiation, so you do not have to worry about
the ID itself. A blank node identifier in JSON-LD always starts with `_:`.

This results in the following JSON-LD output:

```json
{
   "@context": "https://schema.org/",
   "@type": "Person",
   "@id": "_:b0",
   "name": "John Smith",
   "knows": {
      "@type": "Person",
      "name": "Sarah Jane Smith",
      "knows": { "@id": "_:b0" }
   }
}
```

## The model in-depth {#the-model-in-depth}

Each type model, like `Thing`, `Person` or `Event`, must implement the
interfaces `\Brotkrueml\Schema\Core\Model\NodeIdentifierInterface` and
`\Brotkrueml\Schema\Core\Model\TypeInterface`. For convenience, a type
model can also extend the abstract class
`\Brotkrueml\Schema\Core\Model\AbstractType` which implements every
required method.

Each [enumeration type](https://docs.typo3.org/permalink/brotkrueml/schema:enumerations@main), like `GenderType`,
`ItemAvailability` or `OrderStatus`, implement the interface
`\Brotkrueml\Schema\Core\Model\EnumerationInterface`.

One interface is available to "mark" a type model class as a "special type". It
does not require the implementation of additional methods:

-   `\Brotkrueml\Schema\Core\Model\WebPageTypeInterface` for a
    [web page type](https://docs.typo3.org/permalink/brotkrueml/schema:webpage-types@main).

These interfaces can be useful when you want to
[extend the vocabulary](https://docs.typo3.org/permalink/brotkrueml/schema:extending-vocabulary@main).

```plantuml
object Thing
object Event
object OtherTypes
object WebPage
object Enumeration
abstract AbstractType
interface NodeIdentifierInterface
interface TypeInterface
interface WebPageTypeInterface
interface EnumerationInterface

Thing --|> AbstractType
Event --|> AbstractType
OtherTypes --|> AbstractType
WebPage --|> AbstractType
AbstractType --|> TypeInterface
TypeInterface --|> NodeIdentifierInterface
WebPage --|> WebPageTypeInterface
Enumeration --|> EnumerationInterface
```

Inheritance of the type models (namespaces are omitted for better readability)

Each type model delivered with this extension extends the `AbstractType`
class.

### Available type model methods {#available-type-model-methods}

The type models which implement `\Brotkrueml\Schema\Core\Model\TypeInterface`
or extend `\Brotkrueml\Schema\Core\Model\AbstractType` expose the following
methods:

-   **setId($id): static**

    The method sets the unique ID of the model. With the ID, you can
    cross-reference types on the same page or between different pages (and even
    between different web sites) without repeating all the properties.

    It is common to use an [IRI](https://en.wikipedia.org/wiki/Internationalized_Resource_Identifier) as ID like in the above example. Please keep in
    mind that the ID should be consistent between changes of the properties, for
    example, if a person marries and the name is changed. The person is still the
    same, so the IRI should be.

    The IRI is no URL, so it is acceptable to give a "404 Not Found" back if you
    call it in a browser.

    -   **Parameter**

        `NodeIdentifierInterface|string|null $id`: The unique ID to set.

    -   **Return value**

        Reference to the model itself.

-   **getId(): string|null**

    Gets the ID of the type model.

    -   **Parameter**

        none

    -   **Return value**

        A previously set ID or null (if not defined).

-   **setProperty($propertyName, $propertyValue): static**

    Call this method to set a property or overwrite a previously one.

    -   **Parameters**

        -   **`string $propertyName`**

            The property name to set. If the property does not exist in the model,
            an exception is thrown.

        -   **`string|array|bool|TypeInterface|NodeIdentifierInterface|null $propertyValue`**

            The value of the property to set. This can be a string, a boolean,
            another model, a node identifier or an array of strings, booleans or
            models. Also null is possible to clear the property value.

    -   **Return value**

        Reference to the model itself.

-   **addProperty($propertyName, $propertyValue): static**

    Call this method if you want to add a value to an existing one. In the
    example above, you can see that `addProperty()` is used to add a second
    value to the `sameAs` property.

    Calling the `addProperty()` method on a property that has no value
    assigned has the same effect as calling `setProperty()`. So you can
    safely use it, for example, in a loop, to set some values on a property.

    -   **Parameters**

        -   **`string $propertyName`**

            The property name to set. If the property does not exist in the model,
            an exception is thrown.

        -   **`string|array|bool|TypeInterface|NodeIdentifierInterface|null $propertyValue`**

            The value of the property to set. This can be a string, a boolean,
            another model, a node identifier or an array of strings, booleans or
            models. Also null is possible to clear the property value.

    -   **Return value**

        Reference to the model itself.

-   **setProperties($properties): static**

    Set multiple properties at once.

    -   **Parameter**

        -   **`array $properties`**

            The properties to set. The key of the array is the property name, the
            value is the property value. Allowed as values are the same as with the
            method `->setProperty()`.

    -   **Return value**

        Reference to the model itself.

-   **getProperty($propertyName): mixed**

    Get the value of a property.

    -   **Parameter**

        -   **`string $propertyName`**

            The property name to get the value from. If the property name does not
            exist in the model, an exception is thrown.

    -   **Return value**

        The value of the property (string, bool, model, node identifier, array of
        strings, array of models, null).

-   **hasProperty($propertyName): bool**

    Check whether the property name exists in a particular model.

    -   **Parameter**

        -   **`string $propertyName`**

            The property name to check.

    -   **Return value**

        `true`, if the property exists and `false`, otherwise.

-   **clearProperty($propertyName): static**

    Resets the value of the property (set it to `null`).

    -   **Parameter**

        -   **`string $propertyName`**

            The property name to set. If the property does not exist in the model,
            an exception is thrown.

    -   **Return value**

        Reference to the model itself.

-   **getPropertyNames(): array**

    Get the names of all properties of the model.

    -   **Return value**

        Array of all property names of the model.

-   **getType(): string|string\[\]**

    Get the type of the model.

    -   **Return value**

        A string (if it is a single type) or an array of strings (if it is a
        [multiple type](https://docs.typo3.org/permalink/brotkrueml/schema:multiple-types@main)).

### `EnumerationInterface` method {#php-enumerationinterface-method}

An [enumeration type](https://docs.typo3.org/permalink/brotkrueml/schema:enumerations@main) requires to implement the interface
`\Brotkrueml\Schema\Core\Model\EnumerationInterface`.

-   **canonical(): string**

    Returns the canonical value of an enum case. This value is used for
    JSON-LD rendering.

    -   **Return value**

        The canonical value.

## Schema manager {#schema-manager}

The schema manager (class `\Brotkrueml\Schema\Manager\SchemaManager`)
collects the concrete type model objects and prepares them for embedding into
the web page.

The class exposes the following methods:

-   **addType(...$type): self**

    Adds the given type models to the Schema Manager for inclusion on the web
    page.

    -   **Parameter**

        -   **`TypeInterface ...$type`**

            The type model classes with the set properties. These can be also
            "special" types, like a `WebPage` or a `BreadcrumbList`.

    -   **Return value**

        Reference to itself.

-   **hasWebPage(); bool**

    Checks, if a [web page type](https://docs.typo3.org/permalink/brotkrueml/schema:webpage-types@main) is already available.

    -   **Parameter**

        none

    -   **Return value**

        `true`, if a web page type is available, otherwise `false`.

-   **addMainEntityOfWebPage($mainEntity, $isPrioritised = false): self**

    Adds a [main entity](https://docs.typo3.org/permalink/brotkrueml/schema:main-entity-of-web-page@main) to the web page.

    -   **Parameters**

        -   **`TypeInterface $mainEntity`**

            The type model to be added.

        -   **`bool $isPrioritised`**

            Set to `true` to [prioritise](https://docs.typo3.org/permalink/brotkrueml/schema:main-entity-prioritisation@main)
            a main entity.

    -   **Return value**

        Reference to itself.

## Node identifier {#node-identifier-1}

A node identifier (class `\Brotkrueml\Schema\Core\Model\NodeIdentifier`)
holds the ID for a type or a reference.

On instantiation of a NodeIdentifier the ID is given as a string argument into
the constructor.

The class exposes the following method:

-   **getId(): string**

    Returns the ID.

    -   **Parameter**

        none

    -   **Return value**

        The ID as a string.

## Blank node identifier {#blank-node-identifier-1}

A blank node identifier (class `\Brotkrueml\Schema\Core\Model\BlankNodeIdentifier`)
holds the ID for a type or a reference.

On instantiation of a BlankNodeIdentifier the ID is auto-generated and unique
within a request.

The class exposes the following method:

-   **getId(): string**

    Returns the ID.

    -   **Parameter**

        none

    -   **Return value**

        The ID as a string.

## Other useful APIs {#other-useful-apis}

### Boolean data type {#boolean-data-type}

Boolean property values are mapped to the according schema terms
`https://schema.org/True` or `https://schema.org/False`. You can also use
the `\Brotkrueml\Schema\Model\DataType\Boolean` class yourself. It exposes
two public constants:

-   **FALSE**

    Provides the value `https://schema.org/False`.

-   **TRUE**

    Provides the value `https://schema.org/True`.

and one static method:

-   **convertToTerm(bool $value): string**

    This method returns the according schema term.
