---
title: "Sets and lists"
manual: "schema"
version: "main"
permalink: "https://docs.typo3.org/permalink/brotkrueml/schema:sets-lists@main"
source: "Developer/Lists.rst"
rendered: "2026-09-27T17:05:57+00:00"
---

# Sets and lists {#sets-lists}

Target group: **Integrators, Developers**

**Table of Contents**

-   [Introduction](https://docs.typo3.org/permalink/brotkrueml/schema:introduction@main)
-   [Unordered set](https://docs.typo3.org/permalink/brotkrueml/schema:unordered-set@main)
-   [Ordered list](https://docs.typo3.org/permalink/brotkrueml/schema:ordered-list@main)

## Introduction {#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.

> [!NOTE]
> **See also**
>
> -   [Supporting Ordering in schema.org](https://blog.schema.org/2026/06/17/supporting-ordering-in-schema-org/)
> -   [Multiple Values and Collections](https://schema.org/docs/datamodel.html#multiple-values)

## Unordered set {#set}

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

### Usage in PHP {#usage-in-php}

Use the [->addProperty($value)](https://docs.typo3.org/permalink/brotkrueml/schema:confval-abstracttype-addproperty@main) method of the type to add multiple values
to a property:

**EXT:my_extension/Classes/Controller/MyController.php**

```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');

        // ...
    }
}

```

This results in the following output:

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

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

### Usage in Fluid {#usage-in-fluid}

Use the [\<schema:property> view helper](https://docs.typo3.org/permalink/brotkrueml/schema:schema-property-view-helper@main) to add multiple values to a
property:

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

This results in the following output:

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

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

## Ordered list {#list}

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

### With `ItemList` {#with-itemlist}

#### Usage in PHP {#usage-in-php-1}

**EXT:my_extension/Classes/Controller/MyController.php**

```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);

        // ...
    }
}

```

This results in the following output:

```json
{
  "@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"
      }
    ]
  }
}
```

#### Usage in Fluid {#usage-in-fluid-1}

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

This results in the following output:

```json
{
  "@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"
      }
    ]
  }
}
```

### With `@list` {#with-list}

<!-- TODO: no Markdown rendering for "versionadded" -->

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](../Images/Developer/AdminPanelOrderedList.png)

### Usage in PHP {#usage-in-php-2}

Use the `\Brotkrueml\Schema\Core\Model\OrderedList` class and pass the
items on instantiation:

**EXT:my_extension/Classes/Controller/MyController.php**

```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));

        // ...
    }
}

```

This results in the following output:

```json
{
  "@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."
    ]
  }
}
```

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](https://docs.typo3.org/permalink/brotkrueml/schema:blank-node-identifier@main)):

**EXT:my_extension/Classes/Controller/MyController.php**

```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));

        // ...
    }
}

```

This results in the following output:

```json
{
  "@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"
          }
        ]
      }
    }
  ]
}
```

#### Usage in Fluid {#usage-in-fluid-2}

Use the [\<schema:orderedList> view helper](https://docs.typo3.org/permalink/brotkrueml/schema:schema-orderedlist-view-helper@main):

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

This results in the following output:

```json
{
  "@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."
    ]
  }
}
```

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](https://docs.typo3.org/permalink/brotkrueml/schema:blank-node-identifier@main)):

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

> [!NOTE]
> the `reverse="1"` attribute is necessary in this case, as
> `<f:merge>` adds the new item to the beginning of the array.

This results in the following output:

```json
{
  "@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"
          }
        ]
      }
    }
  ]
}
```
