Using The API

Target group: Developers

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

Types

Deprecated since version 3.0.0: Before version 3.0 a type was created with the static method TypeFactory::createType(). This has been deprecated, inject the TypeFactory into the constructor and use TypeFactory->create() instead (like in the example below).

Let's start with a simple example. Imagine you describe a 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

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 vocabulary of schema.org. 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 \DomainException 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.

Surely you will need to add some properties:

EXT:my_extension/Classes/Controller/MyController.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');
        $person
            ->setId('https://example.org/#person-42')
            ->setProperty('givenName', 'John')
            ->setProperty('familyName', 'Smith')
            ->setProperty('gender', 'https://schema.org/Male');

        // ...
    }
}

That was easy ... let's go on and define an event the person attends:

EXT:my_extension/Classes/Controller/MyController.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://twitter.com/fancy-event')
            ->addProperty('sameAs', 'https://facebook.com/fancy-event')
        ;

        // ...
    }
}

Now we have to connect the two types together:

EXT:my_extension/Classes/Controller/MyController.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

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:

{
   "@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://twitter.com/fancy-event", "https://facebook.com/fancy-event"]
   }
}

Multiple types

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

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:

{
   "@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

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

{
   "@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

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
    {
        // ...

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

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

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

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

        $this->schemaManager->addType($person1);
        $this->schemaManager->addType($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

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.

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

EXT:my_extension/Classes/Controller/MyController.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
    {
        // ...

        $nodeIdentifier = new BlankNodeIdentifier();

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

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

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

        $this->schemaManager->addType($person1);
        $this->schemaManager->addType($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:

{
   "@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

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.

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.

These interfaces can be useful when you want to extend the vocabulary.

object Thing
object Event
object OtherTypes
object WebPage
abstract AbstractType
interface NodeIdentifierInterface
interface TypeInterface
interface WebPageTypeInterface

Thing --|> AbstractType
Event --|> AbstractType
OtherTypes --|> AbstractType
WebPage --|> AbstractType
AbstractType --|> TypeInterface
AbstractType --|> NodeIdentifierInterface
WebPage --|> WebPageTypeInterface

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

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

setId($id)

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 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)

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)

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)

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)

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)

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)

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()

Get the names of all properties of the model.

Return value

Array of all property names of the model.

getType()

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).

Schema manager

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

The class exposes the following methods:

addType($type)

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

Parameter
TypeInterface $type

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

Return value

Reference to itself.

hasWebPage()

Checks, if a web page type is already available.

Parameter

none

Return value

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

addMainEntityOfWebPage($mainEntity, $isPrioritised = false)

Adds a main entity to the web page.

Parameters
TypeInterface $mainEntity

The type model to be added.

bool $isPrioritised

Set to true to prioritise a main entity.

Return value

Reference to itself.

Node identifier

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()

Returns the ID.

Parameter

none

Return value

The ID as a string.

Blank node identifier

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()

Returns the ID.

Parameter

none

Return value

The ID as a string.

Other useful APIs

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.