Structured data is essential for search engine optimisation nowadays. This
extension allows the easy integration of structured data based on the
schema.org vocabulary on a TYPO3 website. A good introduction to the topic is
provided by Google: Understand how structured data works.
Structured data is essential for search engine optimisation nowadays. This
extension allows the easy integration of structured data based on the
schema.org vocabulary on a TYPO3 website. A good introduction to the topic is
provided by Google: Understand how structured data works.
The defined structured data is embedded on a web page in JSON-LD markup and
can be checked with the Schema Markup Validator and Google's
Rich Results Test. The JSON-LD generated by this extension can also be
reviewed in the Admin Panel.
Schema.org is a set of extensible schemas that enables webmasters to embed
structured data on their web pages for use by search engines and other
applications. The vocabulary is divided into different sections:
The core section provides all default types and properties, like
Person or Organization.
The pending section is a staging area for work-in-progress terms which
have yet to be accepted into the core vocabulary. Pending terms are subject
to change and should be used with caution. On the schema.org website they
are displayed in a blue colour.
The auto section provides additional terms related to auto.
The bib section provides additional terms related to bibliography.
This TYPO3 schema extension provides the terms for the core section. To
use terms from other sections you can extend the vocabulary on your own as
described in the according chapter or use one
of the additional TYPO3 extensions provided by the author of this extension:
The schema.org vocabulary is evolving, several times a year a new version is
released. The TYPO3 extensions are updated when relevant changes are made to
the according vocabulary. The classes for the type models and view helpers are
generated by the schema-generator library.
You can browse through a list of types available
in a TYPO3 installation.
As an editor you have the possibility to adjust the type of every single web
page for the schema markup. The default value is the most generic one:
WebPage.
You'll find the field in the page properties under the SEO tab
(with installed seo system extension) or under the
Metadata tab (if the seo system extension is not installed):
Field Type of web page in the page properties
If no value is selected, WebPage is assumed.
Available web page types
WebPage is the most common web page type, the other types are more specific:
Type
Description
WebPage
This is the most generic type for a web page
AboutPage
Page about the site, the organization, the person behind the site, etc.
CheckoutPage
Checkout page in a web shop
CollectionPage
Page about multiple things, like a paginated page listing blog posts, a product
category, etc.
MediaGallery
A mixed-media page that can contains media such as images, videos, and other
multimedia
ImageGallery
Page with an image gallery as the most valuable content
VideoGallery
Page with a video gallery
ContactPage
Page with contact information
FAQPage
Page with frequently asked questions
ImageGallery
Page with an image gallery as the most valuable content
ItemPage
Page about a single item, for example, a blog posting, a photograph, a product
MedicalWebPage
Page that provides medical information (with installed extension "schema_health")
ProfilePage
Page for user profiles
QAPage
A page with a question and one or more answers to this question
RealEstateListing
Page listing that describes one or more real-estate offers (with installed
extension "schema_pending")
SearchResultsPage
Page for the result pages of the search function
Installation
Target group: Administrators
Note
The extension in version 4.1 supports TYPO3 v13 LTS and TYPO3 v14.
The recommended way to install this extension is by using Composer. In your
Composer-based TYPO3 project root, just type:
To configure the extension, go to
Admin Tools > Settings > Extension Configuration and click on the
Configure extensions button. Open the schema
configuration:
Options in the extension configuration
Automatic embedding of the WebPage schema into the page
If this option is enabled, the WebPage type schema is automatically embedded
into the page. The web page type can be defined in the field
Specific type of web page of the page properties
and defaults to WebPage.
Default value
enabled
Automatic embedding of the breadcrumb markup into the page
If this option is enabled, the breadcrumb is automatically generated from the
rootline of the current page.
Default value
disabled
Note
Since multiple breadcrumbs are allowed for a page, this option adds a
breadcrumb to the possibly already existing ones (for example, defined via
the API or the
view helpers).
Automatic embedding of the breadcrumb markup into the page - Exclude additional doktypes
The doktypes 199 (spacer), 254 (folder) and 255 (recycler) are always excluded.
Default value
(empty)
Allow only one breadcrumb list
With enabled option only one breadcrumb list will be rendered. This may be
helpful, if the option Automatic embedding of the breadcrumb markup into the page
is enabled and you want to overwrite the generated breadcrumb list on a
dedicated page with a custom one.
Embed markup on "noindex" pages
If this option is enabled, the schema markup is embedded also on "noindex"
pages.
Default value
enabled
Note
The option is considered only if the SEO system extension is installed. If this is not the case, the markup is
always embedded.
TypoScript
Target group: Integrators
Content Object (cObject)
SCHEMA
The extension provides the cObject
SCHEMA. The cObject itself will not
display anything. Instead it will add configured types to the global schema
output.
That will add an element of type WebSite to schema. It will consist of the
property name as well as description.
The name property will be filled from the seo_title field, falling back to
the title field.
Note
Using the
SCHEMA cObject in an array-like structure is not possible
by now, like the following example:
With the help of
<schema:type> view helpers you can define schema markup
in Fluid templates. This can be helpful if you can't use the API,
for example, in third-party extensions.
Each type in the schema.org vocabulary is mapped into an according view helper.
The properties of a type are available as view helper arguments. As you will see
in the example, you can also nest view helpers into each other.
There are currently over 600 view helpers available.
Note
The extension registers schema in the global Fluid namespace. So there is
no need to import the namespace in your templates.
<schema:type> view helpers
Let's start with a simple example. It's the same markup about John Smith as in
the API reference, so you can compare the differences.
Imagine you describe a person on a plugin's detail page that you want to
enrich with structured markup:
Every type view helper starts with
<schema:type.xxx> where
xxx
is the lower camel case variant of the schema.org type name.
Changed in version 3.0
If the type name starts with a number (for example, 3DModel) then the first
number of the view helper is written out (
<schema:type.threeDModel).
The according properties (like givenName and familyName) are attributes.
You can find a list of all available properties for a specific type on the
schema.org page, for example, for the person.
In the example, there are two attributes that begin with a
-. They are
explained in detail in the chapter Special attributes.
As you can see, the value
true (and
false accordingly) can be
used. They are mapped later to the schema types https://schema.org/True and
https://schema.org/False.
Please also recognise the
<schema:property> view helper. With this view
helper you can pass more than one string value to the according type.
Special attributes start with a dash (
-) to separate them
from the common properties of the schema.org specification and to avoid
collisions. Let's have a deeper look on them.
-id
-id
This attribute sets a unique id for the type and is mapped in JSON-LD to the
@id property. The LD in JSON-LD means "linked data". With an @id you
can define a type on one page (for example, Event):
You can also cross-reference the types between different websites. The
@id is globally unique, so a best practise is to use an
IRI for it. It is also
good practise to add the
name property as attribute.
-as
-as
This attribute is used to connect a type to its parent. In the above example,
you can see that the event type view helper uses
-as to connect to
the performerIn property of the person type view helper.
Note
The usage of the attribute makes only sense in a child. If it is used in a
parent type the view helper is ignored.
-specificType
-specificType
Sometimes it can may be helpful to set a specific type. Imagine you have
records of places in the backend where you can select which type of specific
place a record has: for example, Museum, Airport, Park or Zoo. In a
Fluid template you can loop over these records when they are on the same
page. But it is not very convenient to use a
<f:switch> or
<f:if> view helper to choose the correct type. For this scenario you
can benefit from this argument:
When using the
-specificType attribute you can only set the
properties of the original type view helper (here: place), no additional
ones from the specific type.
In the
types argument the types are delimited by commas. Add in the
properties argument the name/value pairs of the according properties.
Here you can mix the properties from the defined types.
The special properties
-as,
-id and
-isMainEntityOfWebPage can also be used as described above.
The example 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."
}
Copied!
You can also use the PropertyViewHelper to
add properties to a multiple type instead the
properties argument:
<schema:multipleType-id="https://example.org/#my-product-and-service"types="Product,Service"
><schema:property-as="name"value="My product and service"/><schema:property-as="manufacturer"value="Acme Ltd."/><schema:property-as="provider"value="Acme Ltd."/></schema:multipleType>
Copied!
<schema:nodeIdentifier> view helper
Sometimes it is useful to reference a node with just the ID. For this case the
<schema:nodeIdentifier> view helper is available:
<f:variablename="identifier1"value="{schema:nodeIdentifier(id: 'https://example.org/#john-smith')}"/><f:variablename="identifier2"value="{schema:nodeIdentifier(id: 'https://example.org/#sarah-jane-smith')}"/><schema:type.personname="John Smith"-id="{identifier1}"knows="{identifier2}"/><schema:type.personname="Sarah Jane Smith"-id="{identifier2}"knows="{identifier1}"/>
The view helper has only one attribute which is required:
id
id
This attribute defines the id and is mapped in JSON-LD to the @id
property.
<schema:blankNodeIdentifier> view helper
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:
<f:variablename="blankIdentifier1"value="{schema:blankNodeIdentifier()}"/><f:variablename="blankIdentifier2"value="{schema:blankNodeIdentifier()}"/><schema:type.personname="John Smith"-id="{blankIdentifier1}"knows="{blankIdentifier2}"/><schema:type.personname="Sarah Jane Smith"-id="{blankIdentifier2}"knows="{blankIdentifier1}"/>
You can only set one string value in the argument of a type view helper, but
sometimes it is necessary to add more than one value to it. There comes the
property view helper into the game:
The
<schema:property> view helper accepts two argument, both are
required.
-as
-as
You know already the
-as attribute from the type view helpers. Its purpose is the same, it references the
property in the parent
<schema:type> view helper.
value
value
The
value argument sets the value of the property, as you guessed
already.
The relevant parts are the namespace declaration
(
xmlns:schema="http://typo3.org/ns/Brotkrueml/Schema/ViewHelpers")
and the
schema:schemaLocation attribute which points to the recent XSD
definition.
With an activated Admin Panel, you can display the
structured data on a page generated by this extension for reviewing:
Types information in the Admin Panel
There are links available: The type to the according Schema.org documentation
and - if available - to the Google and Yandex references for the specific type,
other URLs to their destination.
Additionally, you can copy the markup on the specific page to the clipboard,
for example, to check it against the Schema Markup Validator, or call
Google's Rich Result Test to validate the structured data.
Configuration
To allow non-admin users to access the schema information in the Admin Panel,
you have to configure it in the corresponding
user TSconfig:
admPanel {
enable {
ext-schema = 1
}
}
Copied!
Translation
Target group: Developers, Integrators
All strings are translatable. Translations are managed on Crowdin. Click on
the link or the button below to help translating!
Override translations
You can override translations in the usual way. Have a look into the
according TYPO3 documentation:
Custom translations.
However, if you feel that a translation provided by the translation server could
be improved, please suggest an alternative on Crowdin.
Known problems
Have a look at the issue tracker to see which features and bugs are already
opened. If you encounter any problems, you can open an issue and preferably file
a pull request.
Each type in the schema.org vocabulary corresponds to a PHP model that
provides the available properties. There is also a view helper for each type
that makes it easy to integrate the data into your website via a Fluid template.
Attention should be paid to the following points:
A web page can be characterised by different schema.org types as outlined in
this chapter. The WebPage type is set automatically
if the corresponding
configuration option
is set. But it can always overridden manually with the desired type and
properties. The chapter The WebPage type is dedicated to this topic.
A breadcrumb does not only help the user to recognise the location of a
particular page on the website. It is also helpful for search engines to
understand the structure of your website. Google honors the website operator
for using the breadcrumb schema markup on a page:
It will be shown in the search result snippet.
The main entity of a web page indicates the
primary entity. It can be set separately from a WebPage.
Quick dive-in
The schema.org vocabulary consists of many types, like Person,
Organization, Product, and so on. They are written with an upper letter
at the beginning of the term.
Each type has several properties which characterise the specific type, like
givenName or lastName for a Person. The properties start with a
lower letter at the beginning in the vocabulary.
The most generic type is Thing. Each other type inherits the properties
from one or more other types, e.g: Corporation is a specific type for
Organization and defines a new property. Organization itself is a
specific type of Thing and inherits the properties of Thing and defines
many more properties characterising this type.
You can retrieve the information about a type or property from the URL
https://schema.org/ followed by the term name. (for example,
https://schema.org/Person) or the name of the property
(for example, https://schema.org/givenName).
Models
This extension provides model classes for each type under the PHP namespace
\Brotkrueml\Schema\Model\Type. For example, the type Thing is mapped
to the model
\Brotkrueml\Schema\Model\Type\Thing, which knows about the
according schema.org properties. A property value can be set with an according
method:
The chapter Using The API describes in-depth how to use the models
and the schema manager.
Note
The models were generated from the schema.org definition and will be updated
as the standard evolves.
View helpers
For usage in Fluid templates, each type is mapped to a view helper in the
schema:type namespace. You assign the type properties as view helper
arguments, for example:
<schema:type.thingname="A thing"/>
Copied!
The view helpers can be nested into each other.
The chapter View helpers explains the usage of
the view helpers in detail.
Note
The view helpers were generated from the schema.org definition and will be
updated as the standard evolves.
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
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:
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. 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.
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:
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 _:.
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, 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.
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 implement
\Brotkrueml\Schema\Core\Model\TypeInterface
or extend
\Brotkrueml\Schema\Core\Model\AbstractType expose the following
methods:
setId($id): static
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 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.
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.
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.
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
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
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
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
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
getPropertyNames(): array
Get the names of all properties of the model.
Return value
Array of all property names of the model.
getType(): string|string[]
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).
EnumerationInterface method
An enumeration type requires to implement the interface
\Brotkrueml\Schema\Core\Model\EnumerationInterface.
canonical(): string
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
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
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.
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
getId(): string
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(): string
getId(): string
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:
The schema.org vocabulary provides enumerations types. An enumeration has
one or more members, for example, the GenderType used for the gender property
has the members Male and Female.
These enumerations can be used in your code instead of plain strings. This has
the advantage of avoiding typos because you can use your IDE's capabilities.
Also these members are part of a common vocabulary.
You can find the enums provided by the TYPO3 schema extensions in the
Classes/Model/Enumeration/ folder.
Usage in PHP
Usage in PHP is straightforward, in this example we are using the
\Brotkrueml\Schema\Model\Enumeration\GenderType enum in the
gender property:
The example in the PHP section above can also be adapted for a Fluid template.
We make use of the constant view helper
(which is available since Fluid v2.12):
Not all enumerations defined by schema.org are provided by this extension (or by
the section extensions), but only those who have at least
one member defined by schema.org. You may find a schema.org enumeration type
which references members from the GoodRelations or from other vocabularies.
If you need them, you can create a custom enum.
There are several web page types available to characterise the content of a web
page. A list of the types can be found in the section
Available Web Page Types.
The WebPage type and its descendants (like AboutPage or ImageGallery)
can only appear once on a web page – as opposed to the other types.
This extension defines a new fieldType of web page in the page properties. Choose the appropriate type
for the page and the schema markup is added automatically to the page (if the
corresponding
configuration setting is
activated). If the configuration option is set and the according page has an
expiration date set, the according property expires will be set in the
markup.
But you have various options to set the web page type on your own. This can be
the case, if you want to define the mainEntity
property for a blog article or a product.
But now let's look at code.
Using the API
As you saw in a previous chapter you can use the API to define the
schema for a page. The WebPage type is no exception to that. Define a
WebPage type for a page via API:
If you define a web page on your own, this overrules the page field value of the specific type of web page.
Tip
You don't have to define a web page type, if you only want to set the main
entity of the page. You can also set the main entity independently of the web
page. Have a look at the chapter Main entity.
Using the view helpers
But imagine you don't have the possibility to add PHP code to an extension (for example,
it is a third-party extension). So the view helpers come into the game. Let's
implement the same example as above with view helpers:
As mentioned above, only one web page type can exist on a page. But what happens
if you set more than one web page type? Well, the last call wins the race. So
you can define it in your Extbase action and set it in a Fluid template – the
template wins.
A breadcrumb is an essential part of a web page. It gives the user an idea of
where he is on the web site. He can also navigate to parent pages. But for
search engines a breadcrumb is also essential to understand the structure of a
web site. Last but not least, the breadcrumb is shown in the search result
snippet if structured markup for the breadcrumb is available.
There can also be more than one breadcrumb on a page, Google gives an example in
his guidelines for a
breadcrumb.
Using the API
You can define a breadcrumb with the API as you may already
guessed. For example, you have defined a breadcrumb somewhere:
page = PAGE
page {
# ... some other configuration ...10 = FLUIDTEMPLATE10 {
# ... some other configuration ...
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
special = rootline
as = breadcrumb
}
}
}
}
Copied!
Wouldn't it be cool to use this variable as input for the schema markup without
iterating over the structure? So, let's use the breadcrumb view helper for that:
<schema:breadcrumbbreadcrumb="{breadcrumb}"/>
Copied!
That is it.
The best place to use this view helper is in the template where you generate
the visual representation of the breadcrumb for your users.
Normally the home page is part of the generated breadcrumb, but this is not
necessary for the schema markup - so the home page is omitted. But if you really
want to have it in the markup (or you have a breadcrumb generated on your own
without the home page), you can use the attribute
renderFirstItem and
set it to
1:
If the key
tx_schema_webpagetype is omitted, it defaults to WebPage.
Remarks
The home page should not be included into the markup.
Please keep in mind that according to the Google Structured Data Testing Tool,
only the type BreadcrumbList is allowed for the breadcrumb property -
either the schema.org definition allows strings. Other types than the
BreadcrumbList are ignored by the schema manager.
It is intended that the breadcrumb is not automatically rendered out of the
page structure of your TYPO3 installation, because it is possible to extend
the breadcrumb with own MenuProcessors like in the
news extension.
A WebPage type provides a property mainEntity, which indicates the
primary content of a page. Every type is allowed - although some types does not
make sense (for example, a breadcrumb cannot be the primary content).
Note
Technically, there can be more than one main entity at a time. For example,
if you have a FAQPage you will usually assign more than one question
as mainEntity.
Using the API
The main entity of a web page can be defined with the API. Let's start with an
example that specifies a product as the primary content:
The above example is rendered as JSON-LD. Let's assume the WebPage type is
set to ItemPage - either in the page properties or via the API or a view
helper.
If the WebPage type is not defined because the
appropriate setting
is disabled in the extension configuration, the main entity is rendered as
a root type.
Using the view helpers
You can define the main entity also in a view helper:
You can set the view helper argument
-isMainEntityOfWebPage only in the
main type view helper, not in a child type view helper.
Prioritisation
Main entities can be prioritised. This is sometimes necessary when different
main entities are defined in different places (for example in a controller,
a Fluid page template or in a content element).
Let's look at an example: In a page template for a blog post, the main entity
is defined as type BlogPosting. There are content elements on the page that
display questions and answers for a FAQ. The content element sets the web page
type to 'FAQPage' and also defines the questions as the main entities (to
display as rich snippets on the search results page). However, Google Search
Console shows an error because BlogPosting is not allowed as the main entity
of a FAQPage.
With the API
The main entity of the API example above can be prioritised by setting the
second argument to
true:
Let's look at the example described in the introduction. In a page template, a
BlogPosting type is defined as the main entity of the page:
<!-- This is defined in a page template --><schema:type.blogPosting-isMainEntityOfWebPage="1"name="A blog post"
/>
Copied!
And the FAQ is rendered in the template of a content element. To prioritise
these types,
-isMainEntityOfWebPage is set to 2:
<!-- This is defined in a content element template --><schema:type.fAQPage/><f:foreach="{questions}"as="question"><schema:type.question-isMainEntityOfWebPage="2"name="{question.title}"
/></f:for>
You can enhance the functionality in the schema extension with PSR-14 event
listeners. An event listener receives an event that provides methods for
retrieving and setting dedicated properties.
The event allows to add markup in cases where no controller is available, for
example, if you want to enrich a page with structured data depending on the
doktype of a page.
The event
\Brotkrueml\Schema\Event\RenderAdditionalTypesEvent
provides the following methods:
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\EventListener;
useBrotkrueml\Schema\Event\RenderAdditionalTypesEvent;
useBrotkrueml\Schema\Type\TypeFactory;
useTYPO3\CMS\Core\Attribute\AsEventListener;
useTYPO3\CMS\Frontend\Page\PageInformation;
#[AsEventListener(
identifier: 'my-extension/add-markup-to-article-pages',
)]
final readonly classAddMarkupToArticlePages{
publicfunction__construct(
private TypeFactory $typeFactory,
){}
publicfunction__invoke(RenderAdditionalTypesEvent $event): void{
// The "frontend.page.information" attribute is available since TYPO3 v13.// Use the "frontend.controller" attribute (TSFE) in older TYPO3 versions// to retrieve the page record./** @var PageInformation $pageInformation */
$pageInformation = $event->getRequest()->getAttribute('frontend.page.information');
$page = $pageInformation->getPageRecord();
if ($page['doktype'] !== 12345) {
return;
}
// Only for doktype 12345
$article = $this->typeFactory->create('Article');
$article->setProperty('name', $page['title']);
// ... and set some other properties
$event->addType($article);
}
}
Copied!
The method
__invoke() implements the logic for rendering additional
types. It receives the
RenderAdditionalTypesEvent. You can add as many
types as you like.
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
Sometimes it may be necessary to use properties that are not standardised or
pending, or to add property annotations. Therefore the schema extension
provides a way to extend types.
These additional properties are not only available in the API but
also as arguments in the view helpers.
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 \Brotkrueml\Schema\Core\AdditionalPropertiesInterface. The interface
requires two methods:
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. 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
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.
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:
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:
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.
Create the view helper (optional)
If you have the need for a view helper with that type, you can create one:
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:
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.
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 extension. It serves as a
blueprint for adding own types and view helpers.
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:
The new web page type can now be selected in the page properties:
MedicalWebPage in the list of available web page types
Add a new enumeration
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):
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Schema\Enumeration;
useBrotkrueml\Schema\Core\Model\EnumerationInterface;
enum BusinessEntityType implements EnumerationInterface
{
case Business;
case Enduser;
case PublicInstitution;
case Reseller;
publicfunctioncanonical(): 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);
<?phpdeclare(strict_types=1);
namespaceMyVendor\MyExtension\Schema\Enumeration;
useBrotkrueml\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;
publicfunctioncanonical(): 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!
List of available types
Target group: Developers, Integrators
Introduction
The available schema.org types (like Person, Organization or BlogPosting)
are extended with ongoing versions of the schema.org standard. The
vocabulary can also be extended independently or
by installing additional vocabulary sections.
An overview of the available types can be found in the
System > Configuration module.
Note
The System > Configuration module is available when the
"lowlevel" system extension is installed.
Types overview
To open the types overview, navigate to the
System > Configuration module. In the upper menu bar, select
Schema: Types.
Available types in the Configuration module
There are two groups available:
All types: The available types are listed alphabetically.
Web page types: The available web page types used in the
page properties.
You can quickly look up the available types using the search box.
Deprecations
Introduced in version 3
Direct instantiation of a type model
Direct instantiation of a type model
Deprecated since version
3.11.0
Removed in version
4.0.0
Alternative
Get an instance of a type model via the TypeFactory, see
Types for details. A deprecation log entry is written with
information about the calling class and line number.
None. If you need it use
\Brotkrueml\Schema\Core\Model\AbstractType->getPropertyNames()
and loop over the property names with
\Brotkrueml\Schema\Core\Model\AbstractType->getProperty().
Use
\Brotkrueml\Schema\Manager\SchemaManager->addMainEntityOfWebPage()
instead. See the API.
\Brotkrueml\Schema\Provider\TypesProvider
\Brotkrueml\Schema\Provider\TypesProvider
Deprecated since version
1.7.0
Removed in version
2.0.0
Alternative
Use
\Brotkrueml\Schema\Type\TypeRegistry which is a singleton
and can be instantiated with
GeneralUtility::makeInstance() or
injected with dependency injection.
Since version 3.0 there is no alternative available.
Changelog
All notable changes to this project will be documented in this file.
The deprecated
PSR-14 event RegisterAdditionalTypePropertiesEvent has been removed. As an
alternative create a class implementing AdditionalPropertiesInterface
(which is available since version 3.10.0), see
Register additional properties for details.
Manual instantiation of a type model class
Instantiating a type model class manually with "new" is not supported anymore.
The
\Brotkrueml\Schema\Type\TypeFactory->create() method should be used
instead (which is available since version 3.0.0):
use Brotkrueml\Schema\Model\Type\Event;
+ use Brotkrueml\Schema\Type\TypeFactory;
final class MyController
{
+ public function __construct(+ private readonly TypeFactory $typeFactory,+ ) {}
public function doSomething(): void
{
// ...
- $event = new Event();+ $event = $this->typeFactory->create('Event');
// ...
}
}
Types and view helpers representing enumerations were removed. It is unlikely
that they were used as they had no real purpose. Use real
enumerations instead which are available since version
3.9.0.
Properties moved from core to pending
Over the time some properties moved from the core vocabulary to pending with
newer version of the Schema.org definition (for whatever reason). To avoid
breaking, these properties were reapplied to the corresponding type models.
Those properties have now been removed. If you need some of them, register them
yourself like explained in Register additional properties or
install the schema_pending extension.
Type declarations added to
TypeInterface
Missing return type declarations and type declarations for the argument of the
setId() method have been added. If you do not implement custom type models
directly from the
\Brotkrueml\Schema\Core\Model\TypeInterface, you are
not affected by this change. Otherwise you have to adjust the methods of your
type model classes.
However, implementing a type model directly from the interface is
discouraged and might not work in the future, extend from
\Brotkrueml\Schema\Core\Model\AbstractType instead.
In version 3.0, the compatibility with TYPO3 v10 LTS was removed. Also PHP 8.1
or higher is necessary.
Type model classes
The type models classes were previously registered via a
Configuration/TxSchema/TypeModels.php file. This file is not recognised
anymore, you have to mark a type model class with the attribute
\Brotkrueml\Schema\Attributes\Type now. See the
Adding types section for more information.
Additionally, the static property
$propertyNames of a type model class is
now type-hinted as an array.
+ use Brotkrueml\Schema\Attributes\Type;+ #[Type('MyCustomType')]
final class MyCustomType extends AbstractType
{
- protected static $propertyNames = [+ protected static array $propertyNames = [
// ... the properties ...
]
}
Copied!
View helpers
Custom view helpers need to specify a property which holds the name of the
type:
final class MyCustomTypeViewHelper extends AbstractTypeViewHelper
{
+ protected string $type = 'MyCustomType';
}
Copied!
Type factory
The call of the static method
\Brotkrueml\Schema\Type\TypeFactory::createType()
has been deprecated. Instead, inject the
TypeFactory into the constructor
and use the new
create() method:
<?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 = TypeFactory::createType('Person');+ $person = $this->typeFactory->create('Person');
// ...
}
}
Copied!
From version 1.x to version 2.0
In version 2.0, the compatibility with TYPO3 v9 LTS was removed. Also PHP 7.4
or higher is necessary.
Signal/Slots
The signal/slots were removed:
registerAdditionalTypeProperties
shouldEmbedMarkup
You can migrate the slots easily to the PSR-14 event listeners:
PSR-14 event listener (in Configuration/Services.yaml):
services:# Place here the default dependency injection configurationMyVendor\MyExtension\EventListener\AdditionalPropertiesForPerson:tags:-name:event.listeneridentifier:'myAdditionalPropertiesForPerson'
Copied!
You can find more information about the PSR-14 event listeners in the chapter
PSR-14 events.
For the migration follow the instructions on the
deprecations chapter.
Markup is embedded by default on "noindex" pages
In schema version 1.x the markup was not embedded on "noindex" pages (with
installed SEO system extension). In version 2
the markup is embedded by default also on these pages. You can deactivate this
behaviour in the extension configuration.
Also in version 1.x a PSR-14 event
\Brotkrueml\Schema\Event\ShouldEmbedMarkupEvent was available to change
the default behaviour of not embedding the markup on "noindex" pages. With the
new configuration option this is not necessary anymore and event listeners for
this event must be removed.
Sitemap
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.