Outputting Structured Data in the FE Template
MM data can be supplemented in the source code with so-called “structured data” to make the content easier to analyse — for example by search engines. One of the most well-known catalogues for such markup is available at Schema.org.
Schemas can be created in the encodings RDFa, Microdata, and JSON-LD and embedded
into the page. Up to Contao 4.9, the encoding Contao used was Microdata — since Contao 4.12,
JSON-LD is used.
To add the markup, create a custom template based on metamodels_prerendered.html5 and adapt
it as shown in the following examples for a job posting — see
JobPosting.
The markup can be validated with tools such as:
More on this topic can be found at Search Engine Optimisation (SEO).
Markup with JSON-LD
Note
This is only available from Contao 4.13 with MM 2.3.
1<?php
2
3use Contao\CoreBundle\Routing\ResponseContext\JsonLd\JsonLdManager;
4use Contao\System;
5use Spatie\SchemaOrg\JobPosting;
6use Spatie\SchemaOrg\Organization;
7use Spatie\SchemaOrg\Place;
8use Spatie\SchemaOrg\PostalAddress;
9use Spatie\SchemaOrg\PropertyValue;
10
11$jsonLdGraph = null;
12$responseContext = System::getContainer()->get('contao.routing.response_context_accessor')->getResponseContext();
13if ($responseContext && $responseContext->has(JsonLdManager::class))
14{
15 /** @var JsonLdManager $jsonLdManager */
16 $jsonLdManager = $responseContext->get(JsonLdManager::class);
17 $jsonLdGraph = $jsonLdManager->getGraphForSchema(JsonLdManager::SCHEMA_ORG);
18}
19?>
20<?php if (count($this->data)): ?>
21 <div class="layout_full">
22 <?php foreach ($this->data as $arrItem): ?>
23 <?php
24 // Build Schema.org data.
25 $schemaData = (new JobPosting())
26 ->identifier((new PropertyValue())->propertyID('jobId')->value($arrItem['raw']['id']))
27 ->hiringOrganization((new Organization())->name($arrItem['text']['corporation_name']))
28 ->title($arrItem['text']['name'])
29 ->datePosted(date('Y-m-d', $arrItem['raw']['created_date']))
30 ->jobLocation((new Place())->address((new PostalAddress())->addressCountry($arrItem['text']['country'])))
31 ->description($arrItem['text']['description']);
32 ?>
33 <div class="item <?= $arrItem['class'] ?>">
34 <h2 itemprop="title"><?= $arrItem['text']['title'] ?></h2>
35 <div>
36 <p><strong>Location:</strong><?= $arrItem['text']['city'] ?> <?= $arrItem['text']['region'] ?>
37 </p>
38 </div>
39 ...
40 <div class="actions">
41 <?php if (null !== ($href = $arrItem['actions']['jumpTo']['href'] ?? null)) {
42 $schemaData->url($href);
43 } ?>
44 <?php foreach ($arrItem['actions'] as $action): ?>
45 <?php $this->insert('mm_actionbutton', ['action' => $action]); ?>
46 <?php endforeach; ?>
47 </div>
48 </div>
49 <?php /* Add Schema.org data. */ $jsonLdGraph?->add($schemaData, 'job-' . $arrItem['raw']['id']); ?>
50 <?php endforeach; ?>
51 </div>
52<?php else : ?>
53 <?php $this->block('noItem'); ?>
54 <p class="info"><?= $this->noItemsMsg ?></p>
55 <?php $this->endblock(); ?>
56<?php endif; ?>
Embedding via JSON-LD does require a few extra lines of code, but the markup is separated
from the HTML source used for browser rendering. This makes it easier to adapt existing templates
or extend them with additional markup.
When adding multiple records to the graph — e.g. in an MM list output — passing a unique
identifier is required: $jsonLdGraph?->add($schemaData, <Unique-ID>).
Markup with Microdata
Microdata markup requires more extensive template changes — embedding as JSON-LD is therefore recommended.
1<?php if (count($this->data)): ?>
2 <div class="layout_full">
3 <?php foreach ($this->data as $arrKey => $arrItem): ?>
4 <div class="item <?= $arrItem['class'] ?>" itemscope itemtype="https://schema.org/JobPosting">
5 <h2 itemprop="title"><?= $arrItem['text']['title'] ?></h2>
6 <div>
7 <p><strong>Location:</strong> <span itemprop="jobLocation" itemscope
8 itemtype="https://schema.org/Place">
9 <span itemprop="address" itemscope itemtype="https://schema.org/PostalAddress">
10 <span itemprop="addressLocality"><?= $arrItem['text']['city'] ?></span>
11 <span itemprop="addressRegion"><?= $arrItem['text']['region'] ?></span>
12 </span>
13 </span>
14 </p>
15 </div>
16 ...
17 </div>
18 <?php endforeach; ?>
19 </div>
20<?php else : ?>
21 <?php $this->block('noItem'); ?>
22 <p class="info"><?= $this->noItemsMsg ?></p>
23 <?php $this->endblock(); ?>
24<?php endif; ?>