Skip to content

Commit

Permalink
[FEATURE] Add possibility to register additional schema types
Browse files Browse the repository at this point in the history
Resolves: #38
  • Loading branch information
brotkrueml committed Mar 23, 2020
1 parent 9727589 commit 2ba377f
Show file tree
Hide file tree
Showing 1,251 changed files with 35,403 additions and 33,892 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Possibility to register additional schema types (#38)

### Deprecated
- TypesProvider in favour of TypeRegistry (which now is a singleton)

## [1.6.0] - 2020-03-09

### Added
Expand Down
21 changes: 13 additions & 8 deletions Classes/Aspect/BreadcrumbListAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use Brotkrueml\Schema\Core\Model\AbstractType;
use Brotkrueml\Schema\Manager\SchemaManager;
use Brotkrueml\Schema\Model\Type;
use Brotkrueml\Schema\Utility\Utility;
use Brotkrueml\Schema\Registry\TypeRegistry;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
Expand All @@ -27,17 +27,22 @@ final class BreadcrumbListAspect implements AspectInterface
/** @var ExtensionConfiguration */
private $configuration;

/** @var object|ContentObjectRenderer */
/** @var ContentObjectRenderer */
private $contentObjectRenderer;

/** @var TypeRegistry */
private $typeRegistry;

public function __construct(
TypoScriptFrontendController $controller = null,
ExtensionConfiguration $configuration = null,
ContentObjectRenderer $contentObjectRenderer = null
ContentObjectRenderer $contentObjectRenderer = null,
TypeRegistry $typeRegistry = null
) {
$this->controller = $controller ?: $GLOBALS['TSFE'];
$this->configuration = $configuration ?: GeneralUtility::makeInstance(ExtensionConfiguration::class);
$this->contentObjectRenderer = $contentObjectRenderer ?: GeneralUtility::makeInstance(ContentObjectRenderer::class);
$this->controller = $controller ?? $GLOBALS['TSFE'];
$this->configuration = $configuration ?? GeneralUtility::makeInstance(ExtensionConfiguration::class);
$this->contentObjectRenderer = $contentObjectRenderer ?? GeneralUtility::makeInstance(ContentObjectRenderer::class);
$this->typeRegistry = $typeRegistry ?? GeneralUtility::makeInstance(TypeRegistry::class);
}

public function execute(SchemaManager $schemaManager): void
Expand Down Expand Up @@ -77,8 +82,8 @@ private function buildBreadCrumbList(array $rootLine): Type\BreadcrumbList
{
$breadcrumbList = (new Type\BreadcrumbList());
foreach ($rootLine as $index => $page) {
$givenItemTypeClass = Utility::getNamespacedClassNameForType($page['tx_schema_webpagetype']);
$webPageTypeClass = $givenItemTypeClass ?: Type\WebPage::class;
$givenItemTypeClass = $this->typeRegistry->resolveModelClassFromType($page['tx_schema_webpagetype'] ?? '');
$webPageTypeClass = $givenItemTypeClass ?? Type\WebPage::class;

/** @var AbstractType $itemType */
$itemType = new $webPageTypeClass();
Expand Down
17 changes: 11 additions & 6 deletions Classes/Aspect/WebPageAspect.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use Brotkrueml\Schema\Core\Model\AbstractType;
use Brotkrueml\Schema\Manager\SchemaManager;
use Brotkrueml\Schema\Utility\Utility;
use Brotkrueml\Schema\Registry\TypeRegistry;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
Expand All @@ -27,12 +27,17 @@ final class WebPageAspect implements AspectInterface
/** @var ExtensionConfiguration */
private $configuration;

/** @var TypeRegistry */
private $typeRegistry;

public function __construct(
TypoScriptFrontendController $controller = null,
ExtensionConfiguration $configuration = null
ExtensionConfiguration $configuration = null,
TypeRegistry $typeRegistry = null
) {
$this->controller = $controller ?: $GLOBALS['TSFE'];
$this->configuration = $configuration ?: GeneralUtility::makeInstance(ExtensionConfiguration::class);
$this->controller = $controller ?? $GLOBALS['TSFE'];
$this->configuration = $configuration ?? GeneralUtility::makeInstance(ExtensionConfiguration::class);
$this->typeRegistry = $typeRegistry ?? new TypeRegistry();
}

public function execute(SchemaManager $schemaManager): void
Expand All @@ -50,10 +55,10 @@ public function execute(SchemaManager $schemaManager): void

$type = $this->controller->page['tx_schema_webpagetype'] ?: static::DEFAULT_WEBPAGE_TYPE;

$webPageClass = Utility::getNamespacedClassNameForType($type);
$webPageClass = $this->typeRegistry->resolveModelClassFromType($type);
if ($webPageClass) {
/** @var AbstractType $webPage */
$webPage = GeneralUtility::makeInstance($webPageClass);
$webPage = new $webPageClass();

if ($this->controller->page['endtime']) {
$webPage->setProperty('expires', \date('c', $this->controller->page['endtime']));
Expand Down
31 changes: 23 additions & 8 deletions Classes/Core/Model/AbstractType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

use Brotkrueml\Schema\Event\RegisterAdditionalTypePropertiesEvent;
use Brotkrueml\Schema\Model\DataType\Boolean;
use Brotkrueml\Schema\Utility\Utility;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
Expand All @@ -21,6 +20,15 @@

abstract class AbstractType
{
/**
* The properties of a specific type
* These are defined in the concrete type model class
*
* @var array<string>
* @api
*/
protected static $propertyNames = [];

/**
* The ID of the type (mapped to @id in result)
*
Expand All @@ -29,12 +37,13 @@ abstract class AbstractType
private $id;

/**
* The properties of a specific type: <propertyName> => <propertyValue>
* These are defined in the type model class
* The properties of a specific type with their corresponding value:
* <propertyName> => <propertyValue>
* Also the additional properties added by an event listener are included
*
* @var array
* @var array<string, mixed>
*/
protected $properties = [];
private $properties = [];

/**
* The fully rendered type with all children as array
Expand All @@ -45,10 +54,16 @@ abstract class AbstractType

public function __construct()
{
$this->initialiseProperties();
$this->addAdditionalProperties();
}

protected function addAdditionalProperties(): void
private function initialiseProperties(): void
{
$this->properties = \array_fill_keys(static::$propertyNames, null);
}

private function addAdditionalProperties(): void
{
$cacheEntryIdentifier = 'additionalTypeProperties-' . \str_replace('\\', '_', static::class);
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('tx_schema');
Expand Down Expand Up @@ -138,7 +153,7 @@ private function checkPropertyExists(string $propertyName): void
{
if (!\array_key_exists($propertyName, $this->properties)) {
throw new \DomainException(
sprintf('Property "%s" is unknown for type "%s"', $propertyName, $this->getType()),
\sprintf('Property "%s" is unknown for type "%s"', $propertyName, $this->getType()),
1561829996
);
}
Expand Down Expand Up @@ -309,7 +324,7 @@ public function isEmpty(): bool

private function getType(): string
{
return Utility::getClassNameWithoutNamespace(static::class);
return \substr(\strrchr(static::class, '\\') ?: '', 1);
}

/**
Expand Down
18 changes: 18 additions & 0 deletions Classes/Core/Model/WebPageElementTypeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);

namespace Brotkrueml\Schema\Core\Model;

/*
* This file is part of the "schema" extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

/**
* The interface indicates a WebPageElement type
*/
interface WebPageElementTypeInterface
{
}
55 changes: 32 additions & 23 deletions Classes/Core/ViewHelpers/AbstractTypeViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use Brotkrueml\Schema\Core\Model\AbstractType;
use Brotkrueml\Schema\Core\TypeStack;
use Brotkrueml\Schema\Manager\SchemaManager;
use Brotkrueml\Schema\Utility\Utility;
use Brotkrueml\Schema\Registry\TypeRegistry;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper;

Expand All @@ -24,21 +24,33 @@ abstract class AbstractTypeViewHelper extends ViewHelper\AbstractViewHelper
protected const ARGUMENT_IS_MAIN_ENTITY_OF_WEBPAGE = '-isMainEntityOfWebPage';
protected const ARGUMENT_SPECIFIC_TYPE = '-specificType';

protected $item = [];
protected static $typeModel = '';

protected $isMainEntityOfWebPage = false;
protected $specificType = '';
private $item = [];

protected $parentPropertyName = '';
private $isMainEntityOfWebPage = false;
private $specificTypeModelClassName = '';

private $parentPropertyName = '';

/** @var TypeStack */
protected $stack;
private $stack;

/** @var SchemaManager */
protected $schemaManager;
private $schemaManager;

public function __construct(TypeStack $typeStack = null, SchemaManager $schemaManager = null)
{
if (empty(static::$typeModel)) {
throw new ViewHelper\Exception(
\sprintf(
'%s::$typeModel must be set to the appropriate type model class',
__CLASS__
),
1584715529
);
}

$this->stack = $typeStack ?: GeneralUtility::makeInstance(TypeStack::class);
$this->schemaManager = $schemaManager ?: GeneralUtility::makeInstance(SchemaManager::class);
}
Expand All @@ -52,7 +64,7 @@ public function initializeArguments()
$this->registerArgument(static::ARGUMENT_IS_MAIN_ENTITY_OF_WEBPAGE, 'bool', 'Set to true, if the type is the primary content of the web page', false, false);
$this->registerArgument(static::ARGUMENT_SPECIFIC_TYPE, 'string', 'A specific type of the chosen type. Only the properties of the chosen type are valid');

$modelClassName = Utility::getNamespacedClassNameForType($this->getType());
$modelClassName = static::$typeModel;
/** @var AbstractType $model */
$model = new $modelClassName();
foreach ($model->getPropertyNames() as $property) {
Expand Down Expand Up @@ -91,7 +103,7 @@ public function render()
}
}

protected function checkSpecificTypeAttribute(): void
private function checkSpecificTypeAttribute(): void
{
$specificTypeFromArguments = (string)($this->arguments[static::ARGUMENT_SPECIFIC_TYPE] ?? '');
unset($this->arguments[static::ARGUMENT_SPECIFIC_TYPE]);
Expand All @@ -100,9 +112,10 @@ protected function checkSpecificTypeAttribute(): void
return;
}

$className = '\\Brotkrueml\\Schema\\ViewHelpers\\Type\\' . $specificTypeFromArguments . 'ViewHelper';
$className = GeneralUtility::makeInstance(TypeRegistry::class)
->resolveModelClassFromType($specificTypeFromArguments);

if (!\class_exists($className)) {
if (empty($className) || !\class_exists($className)) {
throw new ViewHelper\Exception(
\sprintf(
'The given specific type "%s" does not exist in the schema.org vocabulary, perhaps it is misspelled? Remember, the type must start with a capital letter.',
Expand All @@ -112,18 +125,18 @@ protected function checkSpecificTypeAttribute(): void
);
}

$this->specificType = $specificTypeFromArguments;
$this->specificTypeModelClassName = $className;
}

protected function checkAsAttribute(): void
private function checkAsAttribute(): void
{
if (!$this->stack->isEmpty()) {
$parentPropertyNameFromArgument = (string)($this->arguments[static::ARGUMENT_AS] ?? '');

if (empty($parentPropertyNameFromArgument)) {
throw new ViewHelper\Exception(
\sprintf(
'The child view helper of schema type "%s" must have an "%s" attribute for embedding into the parent type',
'The child view helper of schema type "%s" must have an "%s" argument for embedding into the parent type',
$this->getType(),
static::ARGUMENT_AS
),
Expand All @@ -137,7 +150,7 @@ protected function checkAsAttribute(): void
unset($this->arguments[static::ARGUMENT_AS]);
}

protected function checkIsMainEntityOfWebPage(): void
private function checkIsMainEntityOfWebPage(): void
{
$this->isMainEntityOfWebPage = (bool)($this->arguments[static::ARGUMENT_IS_MAIN_ENTITY_OF_WEBPAGE] ?? false);

Expand All @@ -155,18 +168,14 @@ protected function checkIsMainEntityOfWebPage(): void
unset($this->arguments[static::ARGUMENT_IS_MAIN_ENTITY_OF_WEBPAGE]);
}

protected function getType(): string
private function getType(): string
{
if (!empty($this->specificType)) {
return $this->specificType;
}

return \str_replace('ViewHelper', '', Utility::getClassNameWithoutNamespace(static::class));
return \substr(\strrchr(static::$typeModel, '\\') ?: '', 1);
}

protected function assignArgumentsToItem(): void
private function assignArgumentsToItem(): void
{
$modelClassName = Utility::getNamespacedClassNameForType($this->getType());
$modelClassName = $this->specificTypeModelClassName ?: static::$typeModel;

/** @var AbstractType $model */
$model = new $modelClassName();
Expand Down
38 changes: 19 additions & 19 deletions Classes/Model/Type/AMRadioChannel.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@
*/
final class AMRadioChannel extends AbstractType
{
protected $properties = [
'additionalType' => null,
'alternateName' => null,
'broadcastChannelId' => null,
'broadcastFrequency' => null,
'broadcastServiceTier' => null,
'description' => null,
'disambiguatingDescription' => null,
'genre' => null,
'identifier' => null,
'image' => null,
'inBroadcastLineup' => null,
'mainEntityOfPage' => null,
'name' => null,
'potentialAction' => null,
'providesBroadcastService' => null,
'sameAs' => null,
'subjectOf' => null,
'url' => null,
protected static $propertyNames = [
'additionalType',
'alternateName',
'broadcastChannelId',
'broadcastFrequency',
'broadcastServiceTier',
'description',
'disambiguatingDescription',
'genre',
'identifier',
'image',
'inBroadcastLineup',
'mainEntityOfPage',
'name',
'potentialAction',
'providesBroadcastService',
'sameAs',
'subjectOf',
'url',
];
}
Loading

0 comments on commit 2ba377f

Please sign in to comment.