Structured data for TYPO3 with the schema extension =================================================== |TYPO3| |Build Status| |Latest Stable Version| Requirements ------------ The extension works with TYPO3 9 LTS. Introduction ------------ 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 structured data is inserted on a web page in `JSON-LD `__ encoding and can be checked with Google’s `Structured Data Testing Tool `__. Just a small desclaimer: Only accepted terms are available, `pending types and properties `__ are not available yet. But if they are integrated into the core vocabulary, they are available within the next update. Installation ------------ Installation With Composer ~~~~~~~~~~~~~~~~~~~~~~~~~~ The recommended way to install this extension is by using Composer. In your Composer based TYPO3 project root, just type :: composer req brotkrueml/schema Installation As An Extension From The TYPO3 Extension Repository (TER) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (to be described) Configuration ------------- Currently there is one extension configuration setting available: ``basic.automaticWebPageSchemaGeneration``: If this option is activated (the default) the `WebPage `__ type schema is automatically inserted into the page if the web page type is not set manually via the API or the view helper. The value of the page field “Specific type of web page” is used as type. The type properties ``name`` (from page title), ``description`` (from page description) and ``expires`` (only if page endtime is set) are defined. Usage ----- The structured data can be defined in two ways: - via an API, e.g. in an Extbase controller - with a view helper in a Fluid template Each type in the schema.org vocabulary corresponds to a PHP model that provides the possible properties. There is also a view helper for each type that makes it easy to integrate the data into your website. Using The API ~~~~~~~~~~~~~ Let’s start with a simple example. Imagine you have some `person `__ on a plugin’s detail page page which you want to enrich with structured data. A good place would be the show action of the controller. First you have to create the schema model: :: $person = new \Brotkrueml\Schema\Model\Type\Person(); Surely you’ll have some properties to add, otherwise it makes no sense: :: $person ->setProperty('givenName', 'John') ->setProperty('familyName', 'Smith') ->setProperty('gender', 'http://schema.org/Male') ; That was easy … let’s go on and add the company for who the person works: :: $corporation = (new \Brotkrueml\Schema\Model\Type\Corporation()) ->setProperty('name', 'Acme Ltd.') ->setProperty('image', 'https:/example.org/logo.png') ->setProperty('url', 'https://example.org/') ->setProperty('sameAs', 'https://twitter.com/example') ->addProperty('sameAs', 'https://facebook.com/example') ; We have to connect the two types together: :: $person->setProperty('worksFor', $corporation); Now we have the data defined. But how do we get them onto the webpage? For that there is the schema manager: :: $schemaManager = TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance( \Brotkrueml\Schema\Manager\SchemaManager::class ); $schemaManager->addType($person); That’s it … if you call the according page the structured data is embedded automatically into the head section: :: { "@context": "http://schema.org", "@type": "Person", "givenName": "John", "familyName": "Smith", "gender": "http://schema.org/Male", "worksFor": { "@type": "Corporation", "name": "Acme Ltd.", "image": "https://example.org/logo.png", "url": "https://example.org", "sameAs": ["https://twitter.com/example", "https://facebook.com/example"] } } WebPage ^^^^^^^ The type `WebPage `__ and its descendants (like `AboutPage `__ or `ImageGallery `__) are a little bit special because they can only appear once on a web page. You can define it with the API: :: $webPage = (new \Brotkrueml\Schema\Model\Type\WebPage()) ->setProperty('name', 'The title of the web page') ->setProperty('description', 'The description of the web page') ->setProperty('primaryImageOfPage', 'https://example.org/image.png') ; $schemaManager->addType($webPage); You can set the web page multiple times, the last one wins. If no web page is defined (and the according configuration setting is activated), a default web page type is created which sets the name (from the page title) and the description (from the page description). Using The View Helpers ~~~~~~~~~~~~~~~~~~~~~~ Embedding View Helpers Into Another ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Another possible way to insert the structured data is via a Fluid template: :: The “-as” property is a special property for the child where you can set the property for the parent type. Please recognise the usage of the ```` view helper. You can use this when assigning more than one string value to a property. Connecting Types Via -id Argument ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ But imagine, you have a page, where the corporation is described with many properties. And you don’t want to repeat the whole data for the corporation. Now comes the LD in JSON-LD into play. The LD means “linked data”. You structure the data for the corporation: :: You recognised the “id” property? This is a special one and is mapped in JSON-LD to “@id” which is a unique IRI which represents this entity. Now you can connect the corporation to the person: :: The resulting output would be now: :: [ { "@context": "http://schema.org", "@type": "Corporation", "@id": "https://example.org/#corporation", "name": "Acme Ltd.", "image": "https://example.org/logo.png", "url": "https://example.org", "sameAs": "https://twitter.com/example" }, { "@context": "http://schema.org", "@type": "Person", "givenName": "John", "familyName": "Smith", "gender": "http://schema.org/Male", "worksFor": { "@type": "Corporation", "@id": "https://example.org/#corporation" } } ] Choosing A Specific Type ^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes it can may be needed to set a specific type. Imagine you’ll have records of places where you can select which type of specific place a record has: e.g. Museum, Airport, Park or Zoo. In a Fluid template you can loop over these records. But it is not very convenient to use a switch view helper to choose the correct type. For this scenario you can benefit from a feature: :: Remarks About The Special Arguments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The dash as a suffix in the view helper argument signals that the argument is not an official schema.org property (-as, -specificType) or has a special meaning (-id transforms to @id). So there should be no collisions with future properties of the schema.org vocabulary. .. _webpage-1: WebPage ^^^^^^^ It is possible to set the WebPage type or one of it descendants with a view helper, e.g. in a Fluid layout: :: or in a News single template (together with the property mainEntity): :: As you can see in this example, you can embed type in type in type (and so on) and also assign the same property (author) multiple times with different values. Breadcrumb ^^^^^^^^^^ A breadcrumb is an essential part of a web page. It gives an user an idea where on the web site he is. He can also navigate to previous 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 `__. You can generate a breadcrumb with the available view helpers of this extension: :: Or a more complex example which respects the web page type: :: It is also possible to use it in combination with a WebPage: :: Most of the time you don’t have the according attributes available in the same Fluid template. Imagine you want to set the breadcrumb in one template or partial and the website in another template (e.g. in a news detail template). You can set the WebPage type and the breadcrumb independently: :: The extension is smart enough to combine them on request. The result is the same for both cases: :: { "@context": "http://schema.org", "@type": "ItemPage", "breadcrumb": { "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "item": { "@type":"WebPage", "@id": "https://example.org/page-1/" }, "name": "Page 1", "position": "1" }, { "@type": "ListItem", "item": { "@type": "CollectionPage", "@id": "https://example.org/page-1/page-2/" }, "name": "Page 2", "position": "2" }, { "@type": "ListItem", "item": { "@type": "ItemPage", "@id": "https://example.org/page-1/page-2/page-3/" }, "name": "Page 3", "position": "3" } ] }, "publisher": { "@type": "Organization", "name": "Acme Ltd." } } If you set the breadcrumb independently of the WebPage, this breadcrumb will be merged with a breadcrumb defined in the WebPage type. Please keep in mind that according to the Google Structured Data Testing Tool, only the type BreadcrumbList is allowed for the breadcrumb - either the schema.org allows strings. Other types than the BreadcrumbList are ignored! But you don’t have to build the breadcrumb markup on your own. TYPO3 has a nice feature called MenuProcessor. :: page = PAGE page.10 = FLUIDTEMPLATE page.10 { dataProcessing { 10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor 10 { special = rootline as = breadcrumb } } } The output of this MenuProcessor can be now used in a Fluid template to populate the breadcrumb automatically with the breadcrumb view helper: :: As default the first item (mostly the home page) is stripped of, because it is not needed. But if you care about it, you can render also the first item: :: You can also build your own breadcrumb array to use in the view helper: It should have the following structure: :: $breadcrumb = [ [ 'title' => 'Home page', 'link' => '/', 'data' => [ 'tx_schema_webpagetype' => 'WebPage', ], ], [ 'title' => 'Videos', 'link' => '/videos/', 'data' => [ 'tx_schema_webpagetype' => 'VideoGallery', ], ], [ 'title' => 'Unicorns in TYPO3 country', 'link' => '/videos/unicorns-in-typo3-country/', 'data' => [ 'tx_schema_webpagetype' => 'ItemPage', ], ], ]; ``data.tx_schema_webpagetype`` can be omitted and defaults to WebPage. .. |TYPO3| image:: https://img.shields.io/badge/TYPO3-9%20LTS-orange.svg :target: https://typo3.org/ .. |Build Status| image:: https://travis-ci.org/brotkrueml/schema.svg?branch=master :target: https://travis-ci.org/brotkrueml/schema .. |Latest Stable Version| image:: https://poser.pugx.org/brotkrueml/schema/v/stable :target: https://packagist.org/packages/brotkrueml/schema