Repeater
The repeater field repeats a small form as a list of items that can be added, sorted, duplicated and removed. On a JSON attribute it saves the items as an array, and on a relation each item is a related record.
Items
form defines the fields of each item, inline or as the path to a fields YAML file. prompt labels the add button, and titleFrom names an item field whose value titles the item's header. Drag the handle to sort the items, check several to drag them together, and use the item menu to duplicate, move or remove one.
fields:
faqs:
label: Questions
type: repeater
prompt: Add question
titleFrom: question
form:
fields:
question:
label: Question
answer:
label: Answer
type: textarea
size: tinyEach item field is named inside the repeater with the item's index, such as faqs[0][question]. The items save as a JSON array of objects, in their order on the page, and saving with no items stores null. The fields of a new item render in the create context and those of a saved item in update, so context works on item fields as it does on any form.
The attribute must hold JSON. On an October Rain model, list it in $jsonable, and on a plain Eloquent model give it an array cast. See Models for both.
/**
* @var array<int, string> jsonable attributes stored as JSON for the structured fields.
*/
protected $jsonable = [
'features',
'permissions',
'interests',
'tags',
'topics',
'content',
'steps',
'address',
'line_items',
'meta',
'shipping',
'contact',
'faqs',
'highlights',
'milestones',
'dimensions',
'sections',
'specs',
'itinerary',
'languages',
'deliveries',
'variants',
'stockists',
'parts',
'price_breaks',
'bundle_items',
'suppliers',
'components',
'stock_levels',
'reviews',
'spec_sheet',
'certifications',
'awards',
'seo',
'packaging',
];Item limits
minItems starts the field with that many empty items, and stops removal below that count. maxItems hides the add button once the field is full. With groups, minItems adds no empty items, since the field cannot tell which group they would be.
fields:
highlights:
label: Highlights
type: repeater
prompt: Add highlight
minItems: 2
maxItems: 4
form:
fields:
text:
label: Highlight
comment: Between two and four highlights.Collapsed items
itemsExpanded: false shows each item as its header only, titled by titleFrom. Opening one item closes the others.
fields:
milestones:
label: Milestones
type: repeater
prompt: Add milestone
titleFrom: title
itemsExpanded: false
form:
fields:
title:
label: Title
span: left
due:
label: Due
span: rightFixed order
showReorder: false removes the drag handle and the move options from the item menu, so the items stay in the order they were added. showDuplicate: false removes Duplicate from the item menu.
fields:
dimensions:
label: Dimensions
type: repeater
prompt: Add dimension
showReorder: false
showDuplicate: false
form:
fields:
label:
label: Label
span: left
value:
label: Value
span: rightGroups
groups replaces form with several item types, and the add button opens a menu of them. Each group is keyed by its code and has a name, a description, an icon and its fields, plus its own titleFrom. groups also takes the path to a YAML file of groups, and a group can be the path to a YAML file of its own.
fields:
sections:
label: Sections
type: repeater
prompt: Add section
groupKeyFrom: type
groups:
text:
name: Text
description: A title and a paragraph
icon: ph ph-text-align-left
titleFrom: title
fields:
title:
label: Title
body:
label: Body
type: textarea
size: tiny
image:
name: Image
description: A caption and how wide the image runs
icon: ph ph-image
titleFrom: caption
fields:
caption:
label: Caption
span: left
alignment:
label: Width
type: dropdown
span: right
options:
inline: Inline
wide: Wide
full: Full widthEach saved item records its group's code under groupKeyFrom, which defaults to _group. Here it is type, so a text item saves as {"title": "...", "body": "...", "type": "text"}.
Builder
displayMode: builder lists the items in a sidebar and edits one at a time beside it, which suits longer items. A group can set useTabs for its own fields, and a group field can be another repeater.
fields:
content:
label: Content Blocks
type: repeater
displayMode: builder
prompt: Add content block
groups:
heading:
name: Heading
description: A title with an optional subtitle
icon: ph ph-text-h
titleFrom: title
fields:
title:
label: Title
span: left
subtitle:
label: Subtitle
span: right
paragraph:
name: Paragraph
description: Text with its options on a second tab
icon: ph ph-paragraph
titleFrom: text
useTabs: true
fields:
text:
label: Text
type: textarea
size: small
tab: Content
align:
label: Alignment
type: radio
inlineOptions: true
tab: Options
default: left
options:
left: Left
center: Center
right: Right
quote:
name: Quote
description: A quote and who said it
icon: ph ph-quotes
titleFrom: author
fields:
quote:
label: Quote
type: textarea
size: tiny
author:
label: Author
checklist:
name: Checklist
description: A nested repeater of checklist items
icon: ph ph-list-checks
fields:
items:
label: Items
type: repeater
prompt: Add item
titleFrom: label
form:
fields:
label:
label: Item
span: left
done:
label: Done
type: checkbox
span: rightTabs
useTabs moves the item fields into tabs, named by each field's tab, so every item has its own set of tabs.
fields:
specs:
label: Specifications
type: repeater
prompt: Add specification
titleFrom: property
useTabs: true
form:
fields:
property:
label: Property
tab: Value
value:
label: Value
span: left
tab: Value
unit:
label: Unit
span: right
tab: Value
note:
label: Note
type: textarea
size: tiny
tab: NotesNested repeaters
An item field can be another repeater. Its items save as an array inside each item, such as [{"day": "...", "stops": [{"place": "..."}]}].
fields:
itinerary:
label: Itinerary
type: repeater
prompt: Add day
titleFrom: day
form:
fields:
day:
label: Day
stops:
label: Stops
type: repeater
prompt: Add stop
titleFrom: place
form:
fields:
place:
label: PlaceWatching the parent form
Item fields are named inside the repeater, so a trigger that watches a field of the parent form starts its name with ^, one for each level up. Here every step's Details field shows while the parent form's Step Style is Detailed.
fields:
step_style:
label: Step Style
type: balloon-selector
default: simple
options:
simple: Simple
detailed: Detailed
steps:
label: Steps
type: repeater
prompt: Add step
titleFrom: title
form:
fields:
title:
label: Title
span: left
minutes:
label: Minutes
type: number
span: right
min: 0
details:
label: Details
type: textarea
size: tiny
trigger:
action: show
field: ^step_style
condition: value[detailed]Relation
When the field name is a relation, each item is a related record, and the item fields are its attributes. Here links is a hasMany relation to SampleLink.
fields:
links:
label: Links
type: repeater
prompt: Add link
titleFrom: title
form:
fields:
title:
label: Title
span: left
url:
label: Address
span: right
comment: Each item is a link record, saved through the hasMany relation./**
* @var array<string, mixed> hasMany relations, the links the repeater edits as records.
*/
public $hasMany = [
'links' => [SampleLink::class, 'delete' => true],
];Adding an item creates an empty record at once, so its columns must accept empty values, and attaches it against the form's session key. Removing an item detaches it the same way. Save commits both and writes each item's fields, and when the related model uses the Sortable trait it also stores the item order. Pass the session key when you save, as Building a Form Page shows. With groups, the group code is stored in the related model's groupKeyFrom column.
<?php
namespace App\Models;
use Database\Factories\SampleLinkFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use October\Rain\Database\Model;
use October\Rain\Database\Traits\Sortable;
/**
* SampleLink is a link on a sample, stored as its own record so the repeater can edit a hasMany relation.
*/
#[Fillable(['title', 'url'])]
class SampleLink extends Model
{
/** @use HasFactory<SampleLinkFactory> */
use HasFactory;
use Sortable;
/**
* @var array<string, mixed> belongsTo relations
*/
public $belongsTo = [
'sample' => Sample::class,
];
}A plain Eloquent model has no deferred binding, so there adding and removing items writes to the database straight away.
Preview
disabled renders the items in preview mode, where they can only be expanded and collapsed, and the field is left out of the save. This copy reads the questions through valueFrom.
fields:
faqs_preview:
label: Questions
type: repeater
valueFrom: faqs
disabled: true
titleFrom: question
form:
fields:
question:
label: Question
answer:
label: Answer
type: textarea
size: tinyOptions
| Option | Description |
|---|---|
| form | the fields of each item, inline or as the path to a fields YAML file. |
| groups | several item types in place of form, inline or as the path to a YAML file. See below. |
| prompt | the add button label. Default: Add New Item. |
| titleFrom | the item field whose value titles each item. With groups, each group's own titleFrom applies instead. Default: none. |
| groupKeyFrom | the key, or the related model's column, that stores each item's group code. Default: _group. |
| minItems | the fewest items, added empty when there are fewer. Default: none. |
| maxItems | the most items, after which the add button hides. Default: none. |
| displayMode | accordion lists the items in place, builder edits them one at a time beside a sidebar. Default: accordion. |
| itemsExpanded | shows every item open. false shows the headers and opens one at a time. Default: true. |
| showReorder | allows sorting the items. Default: true. |
| showDuplicate | adds Duplicate to the item menu. Default: true. |
| useTabs | moves the item fields into tabs, named by each field's tab. Default: false. |
The field also takes the common field properties, such as disabled and valueFrom.
Group options
| Option | Description |
|---|---|
| name | the group name in the add menu. |
| description | a line under the name in the add menu. |
| icon | the group's icon class, such as ph ph-image. Default: the icon pack's default group icon. |
| fields | the group's item fields. |
| titleFrom | the item field whose value titles the group's items. |
| useTabs | moves the group's fields into tabs. Default: the field's useTabs. |