Sets and lists 

Target group: Integrators, Developers

Introduction 

Any property in Schema.org can have multiple values, which are provided in JSON-LD as an array of values. These multiple values are technically treated as an unordered set by default. But there are cases where the order of the values matters, for example, steps in a recipe, or items in a list. Most parsers assume source order, but it is not explicit.

Unordered set 

An unordered set of values can be achieved in the TYPO3 in the following ways.

Usage in PHP 

Use the ->addProperty($value) method of the type to add multiple values to a property:

EXT:my_extension/Classes/Controller/MyController.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Type\TypeFactory;

final class MyController
{
    public function __construct(
        private readonly TypeFactory $typeFactory,
    ) {}

    public function doSomething(): void
    {
        // ...

        $person = $this->typeFactory->create('Person');
        $person->setProperty('name', 'John Smith');
        $person->addProperty('sameAs', 'https://mastodon.example.com/@john-smith');
        $person->addProperty('sameAs', 'https://peertube.example.com/john-smith');

        // ...
    }
}
Copied!

This results in the following output:

{
  "@context": "https://schema.org/",
  "@type": "Person",
  "name": "John Smith",
  "sameAs": [
    "https://mastodon.example.com/@john-smith",
    "https://peertube.example.com/john-smith"
  ]
}
Copied!

The order of the values in the sameAs property does not matter in this example.

Usage in Fluid 

Use the <schema:property> view helper to add multiple values to a property:

<schema:type.person name="John Smith">
   <schema:property -as="sameAs" value="https://mastodon.example.com/@john-smith"/>
   <schema:property -as="sameAs" value="https://peertube.example.com/john-smith"/>
</schema:type.person>
Copied!

This results in the following output:

{
  "@context": "https://schema.org/",
  "@type": "Person",
  "name": "John Smith",
  "sameAs": [
    "https://mastodon.example.com/@john-smith",
    "https://peertube.example.com/john-smith"
  ]
}
Copied!

The order of the values in the sameAs property does not matter in this example.

Ordered list 

You can provide an ordered list as value for a property via the following API.

With ItemList 

Usage in PHP 

EXT:my_extension/Classes/Controller/MyController.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Type\TypeFactory;

final class MyController
{
    public function __construct(
        private readonly TypeFactory $typeFactory,
    ) {}

    public function doSomething(): void
    {
        // ...

        $positiveNotes = [
            'Tougher and water resistant design.',
            'Cheery bright colours and solid feel.',
            'Excellent amplification.',
        ];

        $review = $this->typeFactory->create('Review');
        $review->setProperty('name', 'Megaphone 11 review');

        $itemList = $this->typeFactory->create('ItemList');
        foreach ($positiveNotes as $index => $note) {
            $item = $this->typeFactory->create('ListItem');
            $item->setProperties([
                'position' => $index + 1,
                'name' => $note,
            ]);
            $itemList->addProperty('itemListElement', $item);
        }

        $review->setProperty('positiveNotes', $itemList);

        // ...
    }
}
Copied!

This results in the following output:

{
  "@context": "https://schema.org",
  "@type": "Review",
  "name": "Megaphone 11 review",
  "positiveNotes": {
    "@type": "ItemList",
    "itemListElement": [
      {
        "@type": "ListItem",
        "name": "Tougher and water resistant design.",
        "position": "1"
      },
      {
        "@type": "ListItem",
        "name": "Cheery bright colours and solid feel.",
        "position": "2"
      },
      {
        "@type": "ListItem",
        "name": "Excellent amplification.",
        "position": "3"
      }
    ]
  }
}
Copied!

Usage in Fluid 

<f:variable name="positiveNotes" value="{
   0: 'Tougher and water resistant design.',
   1: 'Cheery bright colours and solid feel.',
   2: 'Excellent amplification.',
}"/>

<schema:type.review name="Megaphone 11 review">
   <schema:type.itemList -as="positiveNotes">
      <f:for each="{positiveNotes}" as="note" iteration="i">
         <schema:type.listItem
            -as="itemListElement"
            position="{i.cycle}"
            name="{note}"
         />
      </f:for>
   </schema:type.itemList>
</schema:type.review>
Copied!

This results in the following output:

{
  "@context": "https://schema.org",
  "@type": "Review",
  "name": "Megaphone 11 review",
  "positiveNotes": {
    "@type": "ItemList",
    "itemListElement": [
      {
        "@type": "ListItem",
        "name": "Tougher and water resistant design.",
        "position": "1"
      },
      {
        "@type": "ListItem",
        "name": "Cheery bright colours and solid feel.",
        "position": "2"
      },
      {
        "@type": "ListItem",
        "name": "Excellent amplification.",
        "position": "3"
      }
    ]
  }
}
Copied!

With @list 

New in version 4.4.0

A property providing an ordered list of values via @list is displayed in the admin panel via the LIST annotation:

Ordered list in the admin panel

Ordered list in the admin panel

Usage in PHP 

Use the OrderedList class and pass the items on instantiation:

EXT:my_extension/Classes/Controller/MyController.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Core\Model\OrderedList;
use Brotkrueml\Schema\Type\TypeFactory;

final class MyController
{
    public function __construct(
        private readonly TypeFactory $typeFactory,
    ) {}

    public function doSomething(): void
    {
        // ...

        $positiveNotes = [
            'Tougher and water resistant design.',
            'Cheery bright colours and solid feel.',
            'Excellent amplification.',
        ];

        $review = $this->typeFactory->create('Review');
        $review->setProperty('name', 'Megaphone 11 review');
        $review->setProperty('positiveNotes', new OrderedList(...$positiveNotes));

        // ...
    }
}
Copied!

This results in the following output:

{
  "@context": "https://schema.org",
  "@type": "Review",
  "name": "Megaphone 11 review",
  "positiveNotes": {
    "@list": [
      "Tougher and water resistant design.",
      "Cheery bright colours and solid feel.",
      "Excellent amplification."
    ]
  }
}
Copied!

If you need to pass a type as list value, create the type and assign an @id to it, then pass the @id as value (for example, via the blank node identifier):

EXT:my_extension/Classes/Controller/MyController.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Brotkrueml\Schema\Core\Model\BlankNodeIdentifier;
use Brotkrueml\Schema\Core\Model\OrderedList;
use Brotkrueml\Schema\Manager\SchemaManager;
use Brotkrueml\Schema\Type\TypeFactory;

final class MyController
{
    public function __construct(
        private readonly SchemaManager $schemaManager,
        private readonly TypeFactory $typeFactory,
    ) {}

    public function doSomething(): void
    {
        // ...

        $positiveNotes = [
            'Tougher and water resistant design.',
            'Cheery bright colours and solid feel.',
            'Excellent amplification.',
        ];

        $review = $this->typeFactory->create('Review');
        $review->setProperty('name', 'Megaphone 11 review');

        $itemIds = [];
        for ($i = 0; $i < \count($positiveNotes); $i++) {
            $itemIds[$i] = new BlankNodeIdentifier();
            $item = $this->typeFactory->create('ListItem');
            $item->setId($itemIds[$i]->getId());
            $item->setProperty('name', $positiveNotes[$i]);
            $this->schemaManager->addType($item);
        }

        $review->setProperty('positiveNotes', new OrderedList(...$itemIds));

        // ...
    }
}
Copied!

This results in the following output:

{
  "@context": "https://schema.org/",
  "@graph": [
    {
      "@type": "ListItem",
      "@id": "_:b0",
      "name": "Tougher and water resistant design."
    },
    {
      "@type": "ListItem",
      "@id": "_:b1",
      "name": "Cheery bright colours and solid feel."
    },
    {
      "@type": "ListItem",
      "@id": "_:b2",
      "name": "Excellent amplification."
    },
    {
      "@type": "Review",
      "name": "Megaphone 11 review",
      "positiveNotes": {
        "@list": [
          {
            "@id": "_:b0"
          },
          {
            "@id": "_:b1"
          },
          {
            "@id": "_:b2"
          }
        ]
      }
    }
  ]
}
Copied!

Usage in Fluid 

Use the <schema:orderedList> view helper:

<f:variable name="positiveNotes" value="{
   0: 'Tougher and water resistant design.',
   1: 'Cheery bright colours and solid feel.',
   2: 'Excellent amplification.',
}"/>

<schema:type.review name="Megaphone 11 review">
   <schema:orderedList -as="positiveNotes" items="{positiveNotes}"/>
</schema:type.review>
Copied!

This results in the following output:

{
  "@context": "https://schema.org",
  "@type": "Review",
  "name": "Megaphone 11 review",
  "positiveNotes": {
    "@list": [
      "Tougher and water resistant design.",
      "Cheery bright colours and solid feel.",
      "Excellent amplification."
    ]
  }
}
Copied!

If you need to pass a type as list value, create the type and assign an @id to it, then pass the @id as value (for example, via the blank node identifier):

<f:variable name="positiveNotes" value="{
   0: 'Tougher and water resistant design.',
   1: 'Cheery bright colours and solid feel.',
   2: 'Excellent amplification.',
}"/>

<f:variable name="orderedList" value="{null}"/>
<f:for each="{positiveNotes}" as="note" reverse="1">
  <f:variable name="id"><schema:blankNodeIdentifier/></f:variable>
  <f:variable name="orderedList"><f:merge array="{0: '{id}'}" with="{orderedList}"/></f:variable>
  <schema:type.listItem -id="{id}" name="{note}"/>
</f:for>

<schema:type.review name="Megaphone 11 review">
   <schema:orderedList -as="positiveNotes" items="{orderedList}"/>
</schema:type.review>
Copied!

This results in the following output:

{
  "@context": "https://schema.org/",
  "@graph": [
    {
      "@type": "ListItem",
      "@id": "_:b0",
      "name": "Excellent amplification."
    },
    {
      "@type": "ListItem",
      "@id": "_:b1",
      "name": "Cheery bright colours and solid feel."
    },
    {
      "@type": "ListItem",
      "@id": "_:b2",
      "name": "Tougher and water resistant design."
    },
    {
      "@type": "Review",
      "name": "Megaphone 11 review",
      "positiveNotes": {
        "@list": [
          {
            "@id": "_:b2"
          },
          {
            "@id": "_:b1"
          },
          {
            "@id": "_:b0"
          }
        ]
      }
    }
  ]
}
Copied!