How can I render complex data struture to template

Silverstripe Version: 4 & 5

Question:
Hi I’m a SS newbie.
How can I render complex data struture like a nested ArrayList/ArrayData (dict/list) to the templates.
I have a poor understanding on https://docs.silverstripe.org/en/5/developer_guides/templates/rendering_templates/

To keep it simple, I found the following code works:

    public function getTestProducts()
    {
        $products = [
            [
                'Name' => 'Product 1',
                'ID' => 1,
            ],
            [
                'Name' => 'Product 2',
                'ID' => 2,
            ],
        ];

        $data = ArrayList::create($products);

        return $data;

    }

while the code belows does not work. I can’t get Stations.

    public function getTestProducts()
    {
        $products = [
            [
                'Name' => 'Product 1',
                'ID' => 1,
                'Stations' => [
                    [
                        'Location' => 'Station A',
                        'Color' => 'Red',
                    ],
                    [
                        'Location' => 'Station B',
                        'Color' => 'Blue',
                    ],
                ],
            ],
            [
                'Name' => 'Product 2',
                'ID' => 2,
            ],
        ];

        $data = ArrayList::create($products);

        return $data;

    }

I want to use it in template like this

<% loop $TestProducts %>
    <h2>$Name</h2>
    <h2>$ID</h2>
    <% loop $Stations %>
        <h3>$Location</h3>
    <% end_loop %>
<% end_loop %>

Besides, can anyone give some examples how to render the data? I can’t understand when I should return a object like code above, when should I use return $this->customise($data), or $this->customise($data)->renderWith().

Wrap each array that represents a record in ArrayData like so:

use SilverStripe\View\ArrayData;

//...

$products = [
    ArrayData::create([
        'Name' => 'Product 1',
        'ID' => 1,
        'Stations' => [
            ArrayData::create([
                'Location' => 'Station A',
                'Color' => 'Red',
            ]),
            ArrayData::create([
                'Location' => 'Station B',
                'Color' => 'Blue',
            ]),
        ],
    ]),
    ArrayData::create([
        'Name' => 'Product 2',
        'ID' => 2,
    ]),
];

You might additionally need to wrap the Stations array in ArrayList::create():

// ...
        'Stations' => ArrayList::create([
            ArrayData::create([
                'Location' => 'Station A',
                'Color' => 'Red',
            ]),
            ArrayData::create([
                'Location' => 'Station B',
                'Color' => 'Blue',
            ]),
        ]),
// ...

The templating system doesn’t know how to render associative arrays - but it does know how to render ViewableData subclasses, such as ArrayData.

If that still doesn’t work, please indicate what errors (if any) you’re getting - check your logs if none appear on screen.

You should use $this->customise($data) when you want to render that data with a specific template that your controller already uses to render.
I think just returning the ArrayList is synonymous with this, but I’m not 100% sure.

You should use $this->customise($data)->renderWith($aTemplate) when you want to render that data with a specific template that your controller doesn’t already use.

Thank you so much! @GuySartorelli

1 Like