Repeater

screenshot

1Repeater::make()
2 ->type('video')
1<x-twill::repeater
2 type="video"
3/>
1@formField('repeater', ['type' => 'video'])
Option Description Type Default value
type Type of repeater items string
name Name of the field string same as type
max Maximum amount that can be created number null (unlimited)
buttonAsLink Displays the Add button as a centered link boolean false
reorder Allow reordering of repeater items boolean true

Repeater fields can be used inside as well as outside the block editor.

Inside the block editor, repeater blocks share the same model as regular blocks. By reading the section on the block editor first, you will get a good overview of how to create and define repeater blocks for your project. No migration is needed when using repeater blocks. Refer to the section titled Adding repeater fields to a block for a detailed explanation.

Outside the block editor, repeater fields are used to save hasMany or morphMany relationships.

Inline repeater

Inline repeaters are Form builder only.

This field will allow you to create repeaters inline. This works for json repeaters or regular repeaters and can both be used in block components and page forms.

While in theory these can be nested, nested inline repeaters only work on blocks not controller forms.

JSON

When using a json repeater in a block, the setup is straightforward and all you have to do is add the inline repeater to your block form.

1<?php
2namespace App\View\Components\Twill\Blocks;
3 
4use A17\Twill\Services\Forms\Fields\Wysiwyg;
5use A17\Twill\Services\Forms\Form;
6use A17\Twill\Services\Forms\Fields\Input;
7use A17\Twill\Services\Forms\InlineRepeater;use A17\Twill\View\Components\Blocks\TwillBlockComponent;
8use Illuminate\Contracts\View\View;
9 
10class Example extends TwillBlockComponent
11{
12 public function render(): View
13 {
14 return view('components.twill.blocks.example');
15 }
16 public function getForm(): Form
17 {
18 return Form::make([
19 InlineRepeater::make()->name('links')
20 ->fields([
21 Input::make()->name('title'),
22 Input::make()->name('url'),
23 ])
24 ]);
25 }
26}

On a regular controller form you have to still setup the handleJsonRepeaters.

Relations

When you are working with relations, you have to setup a little bit more.

Below is a full fledged example coming from the portfolio installable example:

1<?php
2 
3namespace App\Http\Controllers\Twill;
4 
5use A17\Twill\Http\Controllers\Admin\ModuleController as BaseModuleController;
6use A17\Twill\Models\Contracts\TwillModelContract;
7use A17\Twill\Services\Forms\Fields\BlockEditor;
8use A17\Twill\Services\Forms\Fields\Input;
9use A17\Twill\Services\Forms\Fields\Repeater;
10use A17\Twill\Services\Forms\Form;
11use A17\Twill\Services\Forms\InlineRepeater;
12use App\Models\Partner;
13 
14class ProjectController extends BaseModuleController
15{
16 protected function setUpController(): void
17 {
18 $this->setModuleName('projects');
19 }
20 
21 public function getForm(TwillModelContract $model): Form
22 {
23 return Form::make([
24 Input::make()
25 ->translatable()
26 ->name('description'),
27 
28 // Inline repeater that can select existing entries.
29 InlineRepeater::make()
30 ->label('Partners')
31 ->name('project_partner')
32 ->triggerText('Add partner') // Can be omitted as it generates this.
33 ->selectTriggerText('Select partner') // Can be omitted as it generates this.
34 ->allowBrowser()
35 ->relation(Partner::class)
36 ->fields([
37 Input::make()
38 ->name('title')
39 ->translatable(),
40 Input::make()
41 ->name('role')
42 ->translatable()
43 ->required(),
44 ]),
45 Repeater::make()->type('comment'), // Regular repeater using a view.
46 // Regular repeater for creating items without a managed model.
47 InlineRepeater::make()
48 ->name('links')
49 ->fields([
50 Input::make()
51 ->name('title'),
52 Input::make()
53 ->name('url')
54 ]),
55 
56 BlockEditor::make()
57 ]);
58 }
59}

Blade repeater fields

The following example demonstrates how to define a relationship between Team and TeamMember modules to implement a team-member repeater.

  • Create the modules. Make sure to enable the position feature on the TeamMember module:
1php artisan twill:make:module Team
2php artisan twill:make:module TeamMember -P
  • Update the create_team_members_tables migration. Add the team_id foreign key used for the TeamMember—Team relationship:
1public function up()
2{
3 Schema::create('team_members', function (Blueprint $table) {
4 /* ... */
5 
6 $table->foreignId('team_id')
7 ->constrained()
8 ->onUpdate('cascade')
9 ->onDelete('cascade');
10 });
11}
  • Run the migrations:
1php artisan migrate
  • Update the Team model. Define the members relationship. The results should be ordered by position:
1class Team extends Model
2{
3 /* ... */
4 
5 public function members()
6 {
7 return $this->hasMany(TeamMember::class)->orderBy('position');
8 }
9}
  • Update the TeamMember model. Add team_id to the fillable array:
1class TeamMember extends Model
2{
3 protected $fillable = [
4 /* ... */
5 'team_id',
6 ];
7}
  • Update TeamRepository. Override the afterSave and getFormFields methods to process the repeater field:
    • Note: For Polymorphic relationships, use updateRepeaterMorphMany in place of updateRepeater
1class TeamRepository extends ModuleRepository
2{
3 /* ... */
4 
5 public function afterSave($object, $fields)
6 {
7 $this->updateRepeater($object, $fields, 'members', 'TeamMember', 'team-member');
8 parent::afterSave($object, $fields);
9 }
10 
11 public function getFormFields($object)
12 {
13 $fields = parent::getFormFields($object);
14 $fields = $this->getFormFieldsForRepeater($object, $fields, 'members', 'TeamMember', 'team-member');
15 return $fields;
16 }
17}
  • Add the repeater Blade template:

Create file resources/views/twill/repeaters/team-member.blade.php:

1@twillRepeaterTitle('Team Member')
2@twillRepeaterTrigger('Add member')
3@twillRepeaterGroup('app')
4 
5<x-twill::input
6 name="title"
7 label="Title"
8 :required="true"
9/>
10 
11...
  • Add the repeater field to the form:

Update file resources/views/twill/teams/form.blade.php:

1@extends('twill::layouts.form')
2 
3@section('contentFields')
4 ...
5 
6 <x-twill::repeater
7 type="team-member"
8 />
9@stop
  • Finishing up:

Add both modules to your twill.php routes. Add the Team module to your twill-navigation.php config and you are done!

Dynamic repeater titles

In Twill >= 2.5, you can use the @twillRepeaterTitleField directive to include the value of a given field in the title of the repeater items. This directive also accepts a hidePrefix option to hide the generic repeater title:

1@twillRepeaterTitle('Person')
2@twillRepeaterTitleField('name', ['hidePrefix' => true])
3@twillRepeaterTrigger('Add person')
4@twillRepeaterGroup('app')
5 
6<x-twill::input
7 name="name"
8 label="Name"
9 :required="true"
10/>