Extending the vocabulary

Target group: Developers

Introduction

The TYPO3 schema extension ships type models and view helpers with their properties from the core section of the schema.org definitions. However, there are several extensions, like Health and lifesciences or Autos. There are also pending types and properties 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 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

You can use a PSR-14 event listener to add one ore more properties to one or more types. The chapter Register additional properties for a type describes how to register additional properties in detail.

Adding types

Changed in version 3.0.0

You can add additional types for use in the API or as a WebPage type. As an example, in March 2020, schema.org introduces a new VirtualLocation type related to the corona crisis, which was quickly adopted by Google. The type can be used as location in the 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/EventListener/AdditionalPropertiesForPerson.php
    <?php
    
    declare(strict_types=1);
    
    namespace MyVendor\MyExtension\Schema\Type;
    
    use Brotkrueml\Schema\Attributes\Type;
    
    #[Type('VirtualLocation')]
    final class VirtualLocation
    {
        private static array $propertyNames = [
            'additionalType',
            'alternateName',
            'description',
            'disambiguatingDescription',
            'identifier',
            'image',
            'mainEntityOfPage',
            'name',
            'potentialAction',
            'sameAs',
            'subjectOf',
            'url',
        ];
    }
    
    Copied!

    In the example, the class is stored in Classes/Schema/Type of your extension, but you can choose any namespace. 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
    
    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);
    
            // ...
        }
    }
    
    Copied!
  2. 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
    
    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';
    }
    
    Copied!

    Changed in version 3.0

    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
    $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['schema'][]
       = 'MyVendor\\MyExtension\\ViewHelpers\\Schema';
    Copied!

    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.

Add a new WebPage type

Changed in version 3.0.0

If you are responsible for a medical website, the chances are high that you need the 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 in the page properties. Simply follow the steps in the Adding types 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

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 ...
    ];
}
Copied!

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

MedicalWebPage in the list of available web page types

MedicalWebPage in the list of available web page types

Add a new enumeration

New in version 3.9.0

This feature is considered experimental and may change at any time until it is declared stable. However, feedback is welcome.

schema.org provides the ability to use enumerations as values for certain properties. The TYPO3 schema extensions provide the enumerations 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, which suggests using the GoodRelations 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

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;
    }
}
Copied!

All enumeration types must implement the interface Brotkrueml\Schema\Core\Model\EnumerationInterface, 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:

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

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

or in Fluid:

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

which results in:

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

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

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,
        };
    }
}
Copied!