For coders TYPO3 Tech Corner

[PHP] JSON-LD ViewHelper to make Google & Co. happy

[PHP] JSON-LD ViewHelper to make Google & Co. happy

AbstractJsonLdSchemaViewHelper.php:

<?php namespace In2code\In2template\ViewHelpers\JsonLdSchema; use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; /** * Class AbstractJsonLdSchemaViewHelper */ abstract class AbstractJsonLdSchemaViewHelper extends AbstractViewHelper { /** * @var bool */ protected $escapeChildren = false; /** * @var bool */ protected $escapeOutput = false; /** * @param array $arguments * @param string $propertyName * @return mixed|null */ protected static function getProperty(array $arguments, string $propertyName) { $value = null; if (!empty($arguments[$propertyName])) { $value = $arguments[$propertyName]; } elseif (!empty($arguments['properties'][$propertyName])) { $value = $arguments['properties'][$propertyName]; } return $value; } }

AddressViewHelper.php:

<?php namespace In2code\In2template\ViewHelpers\JsonLdSchema; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; /** * Schema.org ViewHelper for rendering an address (not person) in JSON-LD format * See <a href="http://schema.org/address" target="_blank" rel="noreferrer">schema.org/address</a> (example 4, JSON-LD) * * Class AddressViewHelper */ class AddressViewHelper extends AbstractJsonLdSchemaViewHelper { /** * Arguments can be submitted complete with the attribute "properties" or with single values * For argument properties, this keys are relevant: * - name * - description * - city * - zip * - street (with number) * - email * - phone * - url * * @return void */ public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('properties', 'array', 'All properties'); $this->registerArgument('name', 'string', 'Overrules name from properties'); $this->registerArgument('description', 'string', 'Overrules description from properties'); $this->registerArgument('city', 'string', 'Overrules city from properties'); $this->registerArgument('zip', 'string', 'Overrules zip from properties'); $this->registerArgument('street', 'string', 'Overrules street from properties'); $this->registerArgument('email', 'string', 'Overrules email from properties'); $this->registerArgument('image', 'string', 'Overrules image from properties'); $this->registerArgument('phone', 'string', 'Overrules phone from properties'); $this->registerArgument('url', 'string', 'Overrules url from properties'); } /** * @return string */ public function render(): string { return self::renderStatic( $this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext ); } /** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ) { $properties = self::getProperties($arguments); $content = json_encode($properties, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); return '<script type="application/ld+json">' . $content . '</script>'; } /** * @param array $arguments * @return array */ protected static function getProperties(array $arguments): array { $properties = [ '@context' => 'http://schema.org', '@type' => 'LocalBusiness', 'address' => [ '@type' => 'PostalAddress', 'addressLocality' => self::getProperty($arguments, 'city'), 'postalCode' => self::getProperty($arguments, 'zip'), 'streetAddress' => self::getProperty($arguments, 'street') ], 'name' => self::getProperty($arguments, 'name'), 'description' => self::getProperty($arguments, 'description'), 'telephone' => self::getProperty($arguments, 'phone'), 'email' => self::getProperty($arguments, 'email'), 'image' => self::getProperty($arguments, 'image'), 'url' => self::getProperty($arguments, 'url'), ]; return $properties; } }

NewsArticleViewHelper.php:

<?php namespace In2code\In2template\ViewHelpers\JsonLdSchema; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; /** * Schema.org ViewHelper for rendering an address (not person) in JSON-LD format * See <a href="http://pending.schema.org/Article" target="_blank" rel="noreferrer">pending.schema.org/Article</a> (example 2, JSON-LD) * * Class NewsArticleViewHelper */ class NewsArticleViewHelper extends AbstractJsonLdSchemaViewHelper { /** * Arguments can be submitted complete with the attribute "properties" or with single values * For argument properties, this keys are relevant: * - title * - author * - uid * - date * * @return void */ public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('properties', 'array', 'All properties'); $this->registerArgument('title', 'string', 'Overrules title from properties'); $this->registerArgument('author', 'string', 'Overrules author from properties'); $this->registerArgument('uid', 'int', 'Overrules uid from properties'); $this->registerArgument('date', \DateTime::class, 'Overrules date from properties'); } /** * @return string */ public function render(): string { return self::renderStatic( $this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext ); } /** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ) { $properties = self::getProperties($arguments); $content = json_encode($properties, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); return '<script type="application/ld+json">' . $content . '</script>'; } /** * @param array $arguments * @return array */ protected static function getProperties(array $arguments): array { $properties = [ '@context' => 'http://schema.org', '@type' => 'Periodical', 'hasPart' => [ '@id' => 'news' . self::getProperty($arguments, 'uid'), '@type' => 'PublicationVolume', 'volumeNumber' => self::getProperty($arguments, 'uid'), 'hasPart' => [ '@id' => 'newsarticle' . self::getProperty($arguments, 'uid') . '000000', '@type' => 'PublicationIssue', 'datePublished' => self::getReadablePublishedDate($arguments), 'issueNumber' => self::getProperty($arguments, 'uid') . '000000' ] ], 'name' => self::getProperty($arguments, 'title'), 'publisher' => self::getProperty($arguments, 'author') ]; return $properties; } /** * @param array $arguments * @return string like "2018-08-12" */ protected static function getReadablePublishedDate(array $arguments): string { $date = self::getProperty($arguments, 'date'); /** @var \DateTime $date */ if (is_a($date, \DateTime::class)) { return $date->format('Y-m-d'); } return ''; } }

PersonViewHelper.php:

<?php namespace In2code\In2template\ViewHelpers\JsonLdSchema; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; /** * Schema.org ViewHelper for rendering person in JSON-LD format * See <a href="http://schema.org/address" target="_blank" rel="noreferrer">schema.org/address</a> (example 1, JSON-LD) * * Class PersonViewHelper */ class PersonViewHelper extends AbstractJsonLdSchemaViewHelper { /** * Arguments can be submitted complete with the attribute "properties" or with single values * For argument properties, this keys are relevant: * - name (first+lastname) * - city * - zip * - street (with number) * - email * - image * - jobTitle * - phone * - url * * @return void */ public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('properties', 'array', 'All properties'); $this->registerArgument('name', 'string', 'Overrules name from properties'); $this->registerArgument('city', 'string', 'Overrules city from properties'); $this->registerArgument('zip', 'string', 'Overrules zip from properties'); $this->registerArgument('street', 'string', 'Overrules street from properties'); $this->registerArgument('email', 'string', 'Overrules email from properties'); $this->registerArgument('image', 'string', 'Overrules image from properties'); $this->registerArgument('jobTitle', 'string', 'Overrules jobTitle from properties'); $this->registerArgument('phone', 'string', 'Overrules phone from properties'); $this->registerArgument('url', 'string', 'Overrules url from properties'); } /** * @return string */ public function render(): string { return self::renderStatic( $this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext ); } /** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ) { $properties = self::getProperties($arguments); $content = json_encode($properties, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); return '<script type="application/ld+json">' . $content . '</script>'; } /** * @param array $arguments * @return array */ protected static function getProperties(array $arguments): array { $properties = [ '@context' => 'http://schema.org', '@type' => 'person', 'address' => [ '@type' => 'PostalAddress', 'addressLocality' => self::getProperty($arguments, 'city'), 'postalCode' => self::getProperty($arguments, 'zip'), 'streetAddress' => self::getProperty($arguments, 'street') ], 'email' => self::getProperty($arguments, 'email'), 'image' => self::getProperty($arguments, 'image'), 'jobTitle' => self::getProperty($arguments, 'jobTitle'), 'name' => self::getProperty($arguments, 'name'), 'telephone' => self::getProperty($arguments, 'phone'), 'url' => self::getProperty($arguments, 'url'), ]; return $properties; } }

ProductViewHelper.php:

<?php namespace In2code\In2template\ViewHelpers\JsonLdSchema; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; /** * Schema.org ViewHelper for rendering person in JSON-LD format * See <a href="http://schema.org/Product" target="_blank" rel="noreferrer">schema.org/Product</a> (example 2, JSON-LD) * * Class ProductViewHelper */ class ProductViewHelper extends AbstractJsonLdSchemaViewHelper { /** * Arguments can be submitted complete with the attribute "properties" or with single values * For argument properties, this keys are relevant: * - name * - description * - image * * @return void */ public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('properties', 'array', 'All properties'); $this->registerArgument('name', 'string', 'Overrules name from properties'); $this->registerArgument('description', 'string', 'Overrules description from properties'); $this->registerArgument('image', 'string', 'Overrules image string from properties'); } /** * @return string */ public function render(): string { return self::renderStatic( $this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext ); } /** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ) { $properties = self::getProperties($arguments); $content = json_encode($properties, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); return '<script type="application/ld+json">' . $content . '</script>'; } /** * @param array $arguments * @return array */ protected static function getProperties(array $arguments): array { $properties = [ '@context' => 'http://schema.org', '@type' => 'Product', 'description' => self::getProperty($arguments, 'description'), 'name' => self::getProperty($arguments, 'name'), 'image' => self::getProperty($arguments, 'image') ]; return $properties; } }

Back

"Code faster, look at the time" - does this sound familiar to you?

How about time and respect for code quality? Working in a team? Automated tests?

Join us