Two lists often come from different places - orders from a database, customers from an API, countries from a JSON file. The lookup pipe function joins them in the template: lookup('customers', 'id', 'name') finds the row of customers whose id equals the value, and answers its name.

PHP
fragments/lookup_1.php
HTML
fragments/lookup_1.pad
Result
<?php

$customers
= [
[
'id' => 1, 'name' => 'Ann', 'country' => 'NL' ],
[
'id' => 2, 'name' => 'Bob', 'country' => 'BE' ]
];

$orders = [
[
'number' => 101, 'customer_id' => 2 ],
[
'number' => 102, 'customer_id' => 1 ],
[
'number' => 103, 'customer_id' => 7 ]
];

?>
{orders}
<p>Order {$number}: {echo $customer_id | lookup('customers', 'id', 'name') | ?? 'unknown customer'}</p>
{/orders}

Order 101: Bob

Order 102: Ann

Order 103: unknown customer

A value no row has answers nothing, so ?? 'fallback' after it supplies one. key defaults to id, and without a field the answer is the row's first field other than the key.

Where the set comes from

The set is named as a data tag names its rows: a {data} block, an array of the page, or a file in _data/. It is indexed on the key once per request, not once per row, so a long list stays cheap to look in; when the set itself changes, the next lookup indexes it again.

PHP
fragments/lookup_2.php
HTML
fragments/lookup_2.pad
Result
<?php

$customers
= [
[
'id' => 1, 'name' => 'Ann', 'country' => 'NL' ],
[
'id' => 2, 'name' => 'Bob', 'country' => 'BE' ]
];

$orders = [
[
'number' => 101, 'customer_id' => 2 ],
[
'number' => 102, 'customer_id' => 1 ],
[
'number' => 103, 'customer_id' => 7 ]
];

?>
{data 'countries'}
[ {"code": "NL", "label": "Netherlands"}, {"code": "BE", "label": "Belgium"} ]
{/data}

{customers}
<p>{$name} lives in {$country | lookup('countries', 'code', 'label')}</p>
{/customers}

Ann lives in Netherlands

Bob lives in Belgium