Skip to content

Commit

Permalink
[FEATURE] Add node identifier view helpers
Browse files Browse the repository at this point in the history
Resolves: #67
  • Loading branch information
brotkrueml committed Jun 7, 2021
1 parent a808828 commit 9c5d3e9
Show file tree
Hide file tree
Showing 12 changed files with 450 additions and 8 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Node identifier and blank node identifier (#65)
- Node identifier and blank node identifier (#65, #67)
- Multiple types for a node (#64)

### Changed
Expand Down
7 changes: 6 additions & 1 deletion Classes/Core/Model/BlankNodeIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* @psalm-immutable
*/
class BlankNodeIdentifier implements NodeIdentifierInterface
class BlankNodeIdentifier implements NodeIdentifierInterface, \Stringable
{
/**
* The ID of the type (mapped to @id in result)
Expand All @@ -37,4 +37,9 @@ public function getId(): string
{
return $this->id;
}

public function __toString(): string
{
return $this->getId();
}
}
7 changes: 6 additions & 1 deletion Classes/Core/Model/NodeIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* @psalm-immutable
*/
class NodeIdentifier implements NodeIdentifierInterface
class NodeIdentifier implements NodeIdentifierInterface, \Stringable
{
/**
* The ID of the type (mapped to @id in result)
Expand All @@ -33,4 +33,9 @@ public function getId(): string
{
return $this->id;
}

public function __toString(): string
{
return $this->getId();
}
}
32 changes: 27 additions & 5 deletions Classes/Core/ViewHelpers/AbstractTypeViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Brotkrueml\Schema\Core\ViewHelpers;

use Brotkrueml\Schema\Core\Model\NodeIdentifierInterface;
use Brotkrueml\Schema\Core\Model\TypeInterface;
use Brotkrueml\Schema\Core\TypeStack;
use Brotkrueml\Schema\Manager\SchemaManager;
Expand Down Expand Up @@ -45,7 +46,7 @@ public function initializeArguments()
parent::initializeArguments();

$this->registerArgument(static::ARGUMENT_AS, 'string', 'Property name for a child node to merge under the parent node', false, '');
$this->registerArgument(static::ARGUMENT_ID, 'string', 'IRI to identify the node', false, '');
$this->registerArgument(static::ARGUMENT_ID, 'mixed', 'IRI or a node identifier to identify the node', false, '');
$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', false, '');

Expand Down Expand Up @@ -154,10 +155,7 @@ private function assignArgumentsToItem(): void
return;
}

if ($this->arguments[static::ARGUMENT_ID] !== '') {
$this->model->setId($this->arguments[static::ARGUMENT_ID]);
}

$this->assignIdToModel();
unset($this->arguments[static::ARGUMENT_ID]);

foreach ($this->arguments as $name => $value) {
Expand All @@ -178,4 +176,28 @@ private function assignArgumentsToItem(): void
$this->model->setProperty($name, $value);
}
}

public function assignIdToModel(): void
{
$id = $this->arguments[static::ARGUMENT_ID];
if ($id === '') {
return;
}

if (!\is_string($id) && !$id instanceof NodeIdentifierInterface) {
throw new ViewHelper\Exception(
\sprintf(
'The %s argument has to be either a string or an instance of %s, %s given',
static::ARGUMENT_ID,
NodeIdentifierInterface::class,
\get_debug_type($id)
)
);
}

/**
* @psalm-suppress PossiblyNullReference
*/
$this->model->setId($this->arguments[static::ARGUMENT_ID]);
}
}
42 changes: 42 additions & 0 deletions Classes/ViewHelpers/BlankNodeIdentifierViewHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

/*
* 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.
*/

namespace Brotkrueml\Schema\ViewHelpers;

use Brotkrueml\Schema\Core\Model\BlankNodeIdentifier;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;

/**
* ViewHelper for creating a BlankNodeIdentifier.
* Best way it is to assign the value to a variable to use
* it multiple times. You can use that variable to
* assign it to the "-id" property of a type view helper.
* The view helper has no arguments.
*
* = Example =
* <code title="Using the view helper">
* <f:variable name="blankIdentifier1" value="{schema:blankNodeIdentifier()}"/>
* <f:variable name="blankIdentifier2" value="{schema:blankNodeIdentifier()}"/>
* <schema:type.person name="John Smith" -id="{blankIdentifier1}" knows="{blankIdentifier2}"/>
* <schema:type.person name="Sarah Jane Smith" -id="{blankIdentifier2}" knows="{blankIdentifier1}"/>
* </code>
*/
final class BlankNodeIdentifierViewHelper extends AbstractViewHelper
{
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): BlankNodeIdentifier {
return new BlankNodeIdentifier();
}
}
48 changes: 48 additions & 0 deletions Classes/ViewHelpers/NodeIdentifierViewHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

/*
* 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.
*/

namespace Brotkrueml\Schema\ViewHelpers;

use Brotkrueml\Schema\Core\Model\NodeIdentifier;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;

/**
* ViewHelper for creating a NodeIdentifier.
*
* = Examples =
* <code title="Using the view helper directly">
* <schema:type.person name="John Smith" -id="{schema:nodeIdentifier(id: 'https://example.org/#john-smith')}"/>
* </code>
* <code title="Using the view helper via variable">
* <f:variable name="identifier1" value="{schema:nodeIdentifier(id: 'https://example.org/#john-smith')}"/>
* <f:variable name="identifier2" value="{schema:nodeIdentifier(id: 'https://example.org/#sarah-jane-smith')}"/>
* <schema:type.person name="John Smith" -id="{identifier1}" knows="{identifier2}"/>
* <schema:type.person name="Sarah Jane Smith" -id="{identifier2}" knows="{identifier1}"/>
* </code>
*/
final class NodeIdentifierViewHelper extends AbstractViewHelper
{
public function initializeArguments()
{
parent::initializeArguments();

$this->registerArgument('id', 'string', 'The identifier for the node', true);
}

public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): NodeIdentifier {
return new NodeIdentifier($arguments['id']);
}
}
103 changes: 103 additions & 0 deletions Documentation/Developer/ViewHelpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,109 @@ which results in the output:
}
.. index::
single: Node identifier view helper

.. _schema-nodeIdentifier-view-helper:

:html:`<schema:nodeIdentifier>` view helper
===========================================

Sometimes it is useful to reference a node with just the ID. For this case the
:html:`<schema:nodeIdentifier>` view helper is available:

.. code-block:: html

<f:variable name="identifier1" value="{schema:nodeIdentifier(id: 'https://example.org/#john-smith')}"/>
<f:variable name="identifier2" value="{schema:nodeIdentifier(id: 'https://example.org/#sarah-jane-smith')}"/>
<schema:type.person name="John Smith" -id="{identifier1}" knows="{identifier2}"/>
<schema:type.person name="Sarah Jane Smith" -id="{identifier2}" knows="{identifier1}"/>

This generates the following JSON-LD:

.. code-block:: json
:emphasize-lines: 6,9,14,17
{
"@context": "https://schema.org/",
"@graph": [
{
"@type": "Person",
"@id": "https://example.org/#john-smith",
"name": "John Smith",
"knows": {
"@id": "https://example.org/#sarah-jane-smith"
}
},
{
"@type": "Person",
"@id": "https://example.org/#sarah-jane-smith",
"name": "Sarah Jane Smith",
"knows": {
"@id": "https://example.org/#john-smith"
}
}
]
}
The view helper has only one attribute which is required:

.. option:: id

This attribute defines the id and is mapped in JSON-LD to the ``@id`` property.


.. index::
single: Blank node identifier view helper

.. _schema-blankNodeIdentifier-view-helper:

:html:`<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:

.. code-block:: html

<f:variable name="blankIdentifier1" value="{schema:blankNodeIdentifier()}"/>
<f:variable name="blankIdentifier2" value="{schema:blankNodeIdentifier()}"/>
<schema:type.person name="John Smith" -id="{blankIdentifier1}" knows="{blankIdentifier2}"/>
<schema:type.person name="Sarah Jane Smith" -id="{blankIdentifier2}" knows="{blankIdentifier1}"/>

This generates the following JSON-LD:

.. code-block:: json
:emphasize-lines: 6,9,14,17
{
"@context": "https://schema.org/",
"@graph": [
{
"@type": "Person",
"@id": "_:b0",
"name": "John Smith",
"knows": {
"@id": "_:b1"
}
},
{
"@type": "Person",
"@id": "_:b1",
"name": "Sarah Jane Smith",
"knows": {
"@id": "_:b0"
}
}
]
}
The view helper has no arguments.

You can find more information in the :ref:`Blank node identifier API section
<blank-node-identifier>`.


.. index:: property view helper

:html:`<schema:property>` view helper
Expand Down
27 changes: 27 additions & 0 deletions Tests/Fixtures/Model/Type/Person.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

/*
* 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.
*/

namespace Brotkrueml\Schema\Tests\Fixtures\Model\Type;

use Brotkrueml\Schema\Core\Model\AbstractType;

class Person extends AbstractType
{
/**
* @var string[]
*/
protected static $propertyNames = [
'name',
'knows',
'url',
'worksFor',
];
}
15 changes: 15 additions & 0 deletions Tests/Unit/Core/Model/BlankNodeIdentifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public function subjectIsInstanceOfNodeIdentifierInterface(): void
self::assertInstanceOf(NodeIdentifierInterface::class, new BlankNodeIdentifier());
}

/**
* @test
*/
public function subjectIsInstanceOfStringableInterface(): void
{
self::assertInstanceOf(\Stringable::class, new BlankNodeIdentifier());
}

/**
* @test
*/
Expand Down Expand Up @@ -59,4 +67,11 @@ public function getIdOnConsecutiveInstantiationsReturnsAscendingBlankIdentifiers

self::assertSame($expected, $actual);
}

public function toStringReturnsBlankIdentifier(): void
{
$subject = new BlankNodeIdentifier();

self::assertSame('_:b0', $subject->__toString());
}
}
15 changes: 15 additions & 0 deletions Tests/Unit/Core/Model/NodeIdentifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ public function subjectIsInstanceOfNodeIdentifierInterface(): void
self::assertInstanceOf(NodeIdentifierInterface::class, new NodeIdentifier('some-id'));
}

/**
* @test
*/
public function subjectIsInstanceOfStringableInterface(): void
{
self::assertInstanceOf(\Stringable::class, new NodeIdentifier('some-id'));
}

/**
* @test
*/
Expand All @@ -34,4 +42,11 @@ public function getIdReturnsCorrectId(): void

self::assertSame('some-id', $subject->getId());
}

public function toStringReturnsBlankIdentifier(): void
{
$subject = new NodeIdentifier('some-id');

self::assertSame('some-id', $subject->__toString());
}
}
Loading

0 comments on commit 9c5d3e9

Please sign in to comment.