FieldtypeRepeater

Stores a collection of repeating field groups

Each item in the collection is a RepeaterPage (extends Page), and the collection is a RepeaterPageArray (extends PageArray). Items are stored as real pages in the ProcessWire admin page tree under page/repeaters/ (relative to your admin root, which is /processwire/ by default but is configurable).

Value type

FieldsetPage (extends RepeaterPage, which extends Page). Always a single object — never a PageArray. The fieldset page is created automatically when the field is first used.

Getting and setting values
// Access fields on the fieldset page
echo $page->fieldset_field->title;
echo $page->fieldset_field->some_other_field;

// Set a field value
$page->fieldset_field->title = 'New value';
$page->save('fieldset_field');

// Check if a subfield is populated
if($page->fieldset_field->title) { ... }

// Access the owner page and field
$ownerPage  = $page->fieldset_field->getForPage();
$ownerField = $page->fieldset_field->getForField();
$ownerTitle = $page->fieldset_field->getForPage()->title;
Adding, removing, and saving items
// Always turn off output formatting when manipulating items
$page->of(false);

// Add a new item
$item = $page->repeater_field->getNewItem();
$item->title = 'New item';
$item->save();
$page->save('repeater_field'); // saves item order, count, and membership

// Remove an item
$page->repeater_field->remove($item);
$page->save('repeater_field');

// Reorder items: manipulate the RepeaterPageArray, then save the parent page
// The order of the array is what determines saved order.
$items = $page->repeater_field;

$itemA = $items->first();
$itemB = $items->last();
$items->append($itemA);                   // move first item to end
$items->prepend($itemB);                  // move last item to beginning
$items->insertBefore($itemA, $itemB);     // move $itemA to just before $itemB
$items->insertAfter($itemA, $itemB);      // move $itemA to just after $itemB
$items->reverse();                        // reverse the order of all items
$items->sort('title');                    // sort items by a sub-field value

$page->save('repeater_field'); // persists the new order

// Add multiple items, then save the parent page once at the end
$rows = [
    [ 'title' => 'First item',  'body' => '<p>Hello</p>' ],
    [ 'title' => 'Second item', 'body' => '<p>World</p>' ],
];
foreach($rows as $data) {
    $item = $page->repeater_field->getNewItem();
    foreach($data as $key => $value) $item->set($key, $value);
    $item->save();
}
$page->save('repeater_field');

Save workflow: $item->save() writes the item's field values to the database. $page->save('repeater_field') updates the parent page's field record (item order, count, and which items belong to it). Both calls are required when adding items; only $item->save() is needed for in-place edits to an existing item.

Creating a repeater field programmatically

The non-obvious part is that a repeater field requires a dedicated template and fieldgroup to hold its sub-fields. Call _getRepeaterTemplate() after saving the field — it creates the template/fieldgroup automatically if they don't exist yet.

$fields    = $wire->fields;
$templates = $wire->templates;

// 1. Create and save the repeater field
/** @var RepeaterField $field */
$field = $fields->new('repeater', 'my_repeater', 'My Repeater');
// ------------------- ^Fieldtype --- ^Name -------- ^Label

// 2. Get/create the repeater's internal template and fieldgroup
$fieldgroup = $field->getRepeaterFieldgroup();

// 3. Add sub-fields to the repeater's fieldgroup
$fieldgroup->add('title');
$fieldgroup->add('body');
$fieldgroup->save();

// 4. Add the repeater field to your template
$template = $templates->get('your-template');
$template->fieldgroup->add($field);
$template->fieldgroup->save();

Notes on programmatic creation:

  • $field->getRepeaterTemplate() creates and returns the repeater's internal template and fieldgroup if they don't exist yet, and stores template_id on the field. Always call this after creating the field.
  • The repeater template is named repeater_{field_name} automatically.
  • Sub-fields are added directly to $repeaterTemplate->fieldgroup — the fieldgroup membership IS the field list.
  • $field->getRepeaterParent() returns the parent page under which repeater item pages are stored (e.g. /processwire/page/repeaters/for-field-123/).
  • $field->getBlankRepeaterPage($page) returns a new unsaved repeater item page for the given owner page — useful when creating items without going through getNewItem().
Selectors

Supports the same subfield selectors as FieldtypeRepeater:

$pages->find('fieldset_field.title*=keyword');
$pages->find('fieldset_field.some_field=value');
Output / markup
// Iterate and output
foreach($page->repeater_field as $item) {
    echo "<h3>{$item->headline}</h3>";
    echo "<p>{$item->body}</p>";
}

// Template syntax with each()
echo $page->repeater_field->each("<div><h3>{headline}</h3><p>{body}</p></div>");

// Depth-aware output (when repeaterDepth field setting is enabled)
foreach($page->repeater_field as $item) {
    $indent = str_repeat('  ', $item->depth);
    echo $indent . $item->title . "\n";
}
Notes
  • The value is always a single FieldsetPage instance, never a PageArray.
  • Fields on the fieldset page exist in their own namespace, so title on one fieldset is independent of title on another — even if both fieldsets use the same field.
  • The fieldset page mirrors the output formatting (of()) state of its owner page automatically.
  • Compatible fieldtypes: FieldtypeFieldsetPage only.
API reference: methods, properties

RepeaterField is used instead of Field for fields of type FieldtypeRepeater.

Please see the Repeater Fieldtype for usage instructions.

Click any linked item for full usage details and examples. Hookable methods are indicated with the icon. In addition to those shown below, the RepeaterField class also inherits all the methods and properties of: Field, WireData and Wire.

Show class?     Show args?       Only hookable?    

Properties

NameReturnSummary 
RepeaterField::accordionMode bool int Allow only one item open at a time? 
RepeaterField::familyFriendly bool int Treat item depth as parent/child relationships in the editor? 
RepeaterField::familyToggle bool int Opening/closing an item also opens/closes its visual children (requires familyFriendly)? 
RepeaterField::lazyParents int bool Avoid creating parent pages when there are no items to store? 
RepeaterField::loudControls bool int Always show item controls regardless of hover state? 
RepeaterField::noScroll bool int Do not scroll to newly added items? 
RepeaterField::parent_id int Parent page ID for repeater items. 
RepeaterField::rememberOpen bool int Remember which items are open between page-edit requests? 
RepeaterField::repeaterAddLabel string Label to use for the "add item" button. 
RepeaterField::repeaterCollapse int Item collapse state; see FieldtypeRepeater::collapse* constants. 
RepeaterField::repeaterDepth int Maximum allowed depth for repeater items (0=disabled). 
RepeaterField::repeaterFields array Array of field IDs used in repeater. 
RepeaterField::repeaterLoading int Dynamic (ajax) loading mode; see FieldtypeRepeater::loading* constants. 
RepeaterField::repeaterMaxItems int Maximum number of items allowed (0=no limit). 
RepeaterField::repeaterMinItems int Minimum number of items required (0=no minimum). 
RepeaterField::repeaterReadyItems int (deprecated) 
RepeaterField::repeaterTitle string Field name or {field} format string to use for repeater item labels. 
RepeaterField::template_id int Template ID used by repeater items. 
RepeaterField::type FieldtypeRepeater 

Additional methods and properties

In addition to the methods and properties above, RepeaterField also inherits the methods and properties of these classes:

API reference based on ProcessWire core version 3.0.259