1Browser::make()2 ->modules([Publications::class])3 ->name('publications')4 ->max(4);
1<x-twill::browser2 module-name="publications"3 name="publications"4 label="Publications"5 :max="4"6/>
1@formField('browser', [2 'moduleName' => 'publications',3 'name' => 'publications',4 'label' => 'Publications',5 'max' => 4,6])
Option | Description | Type | Default value |
---|---|---|---|
name | Name of the field | string | |
label | Label of the field | string | |
moduleName | Name of the module (single related module) (use the modules method with a single class for the FormBuilder) |
string | |
modules | Array of modules (multiple related modules), must include name or be a module class | array | |
endpoints | Array of endpoints (multiple related modules), must include value and label | array | |
params | Array of query params (key => value) to be passed to the browser endpoint | array | |
max | Max number of attached items | integer | 1 |
note | Hint message displayed in the field | string | |
fieldNote | Hint message displayed above the field | string | |
browserNote | Hint message displayed inside the browser modal | string | |
itemLabel | Label used for the Add button |
string | |
buttonOnTop | Displays the Add button above the items |
boolean | false |
wide | Expands the browser modal to fill the viewport | boolean | false |
sortable | Allows manually sorting the attached items | boolean | true |
disabled | Disables the field | boolean | false |
connectedBrowserField | Name of another browser field to connect to | string |
Browser fields can be used inside as well as outside the block editor.
Inside the block editor, no migration is needed when using browsers. Refer to the section titled Adding browser fields to a block for a detailed explanation.
Outside the block editor, browser fields are used to save belongsToMany
relationships. The relationships can be stored
in Twill's own twill_related
table or in a custom pivot table.
The following example demonstrates how to use a browser field to attach Authors
to Articles
.
Article
model to add the HasRelated
trait:1use A17\Twill\Models\Behaviors\HasRelated;2 3class Article extends Model4{5 use HasRelated;6 7 /* ... */8}
ArticleRepository
to add the browser field to the $relatedBrowsers
property:1class ArticleRepository extends ModuleRepository2{3 protected $relatedBrowsers = ['authors'];4}
resources/views/twill/articles/form.blade.php
: 1@extends('twill::layouts.form') 2 3@section('contentFields') 4 ... 5 6 <x-twill::browser 7 module-name="authors" 8 name="authors" 9 label="Authors"10 :max="4"11 />12@stop
In some cases you may want to have 2 browser fields pointing to the same module. As by default the array elements
in $relatedBrowsers
expect the module name and field name to match we can work around this.
With the following form:
1$form->add(2 Browser::make()->modules([Page::class])->name('page_1'),3);4 5$form->add(6 Browser::make()->modules([Page::class])->name('page_2'),7);
We can setup $relatedBrowsers
like this:
1protected $relatedBrowsers = [ 2 'page_1' => [ 3 'moduleName' => 'pages', 4 'relation' => 'page_1' 5 ], 6 'page_2' => [ 7 'moduleName' => 'pages', 8 'relation' => 'page_2' 9 ]10];
You can use the same approach to handle polymorphic relationships through Twill's related
table.
ArticleRepository
:1class ArticleRepository extends ModuleRepository2{3 protected $relatedBrowsers = ['collaborators'];4}
resources/views/twill/articles/form.blade.php
: 1@extends('twill::layouts.form') 2 3@section('contentFields') 4 ... 5 6 <x-twill::browser 7 :modules="[ 8 [ 9 'label' => 'Authors',10 'name' => 'authors',11 ],12 [13 'label' => 'Editors',14 'name' => 'editors',15 ],16 ]"17 name="collaborators"18 label="Collaborators"19 :max="4"20 />21@stop
1<x-twill::browser 2 :endpoints="[ 3 [ 4 'label' => 'Authors', 5 'value' => '/authors/browser', 6 ], 7 [ 8 'label' => 'Editors', 9 'value' => '/editors/browser',10 ],11 ]"12 name="collaborators"13 label="Collaborators"14 :max="4"15/>
To retrieve the items in the frontend, you can use the getRelated
method on models and blocks. It will return of
collection of related models in the correct order:
1$item->getRelated('collaborators');2 3// or, in a block:4 5$block->getRelated('collaborators');
The following example demonstrates how to make a browser field depend on the selected items of another browser field.
1<x-twill::browser 2 module-name="products" 3 name="product" 4 label="Product" 5 :max="1" 6/> 7 8<x-twill::browser 9 label="Product variant"10 name="product_variant"11 module-name="productVariants"12 connected-browser-field="product"13 note="Select a product to enable this field"14 :max="1"15/>
The second browser is using the connectedBrowserField
option, which will:
connectedBrowserIds
query
parameter,From your module's controller, you can then use connectedBrowserIds
to do something like:
1public function getBrowserData($prependScope = [])2{3 if ($this->request->has('connectedBrowserIds')) {4 $products = collect(json_decode($this->request->get('connectedBrowserIds')));5 $prependScope['product_id'] = $products->toArray();6 }7 8 return parent::getBrowserData($prependScope);9}
In the presented example, this will make sure only variants of the selected product in the first browser can be selected in the second one.
Using the HasRelated
trait means that Twill is storing the relationship in a polymorphic table, which can make efficient database queries harder to implement depending on the needs of your frontend. Using a pivot table between 2 Twill models is available if you need it.
1Schema::create('artist_artwork', function (Blueprint $table) {2 createDefaultRelationshipTableFields($table, 'artist', 'artwork');3 $table->integer('position')->unsigned()->nullable();4 });
Artist
):1public function artworks(): BelongsToMany2{3 return $this->belongsToMany('artworks')->orderByPivot('position');4}
$browsers
property in your repository (i.e. ArtistRepository
):1protected $browsers = ['artworks'];
Additional parameters can also be overridden with an array. When only the browser name is given, the rest of the parameters are inferred from the name.
1protected $browsers = [2 'artworks' => [3 'titleKey' => 'title',4 'relation' => 'artworks',5 'browserName' => 'artworks',6 'moduleName' => 'artworks',7 'positionAttribute' => 'position',8 ],9];
For even more control, you can use updateBrowser() in your own afterSave()
method and getFormFieldsForBrowser() in your own getFormFields()
method.
While a bit more complex to setup, you can target a morphTo.
For example we have a MenuItem
model, and we want to target multiple types of models in our system.
In our MenuItem
we add the relation to our linkable
:
1public function linkable()2{3 return $this->morphTo();4}
This goes with the following migration on the menu_item
:
1$table->bigInteger('linkable_id')->nullable();2$table->string('linkable_type')->nullable();
Then in our MenuItemRepository
we have to setup a few things:
1// Prepare the fields. 2public function prepareFieldsBeforeCreate($fields) 3{ 4 $fields = parent::prepareFieldsBeforeCreate($fields); 5 $fields['linkable_id'] = Arr::get($fields, 'browsers.linkables.0.id', null); 6 $fields['linkable_type'] = Arr::get($fields, 'browsers.linkables.0.endpointType', null); 7 8 return $fields; 9}10 11// On save we set the linkable id and type.12public function prepareFieldsBeforeSave($object, $fields)13{14 $fields = parent::prepareFieldsBeforeSave($object, $fields);15 16 $id = Arr::get($fields, 'browsers.linkables.0.id', null);17 $type = Arr::get($fields, 'browsers.linkables.0.endpointType', null);18 19 if ($id) {20 $fields['linkable_id'] = $id;21 }22 if ($type) {23 $fields['linkable_type'] = $type;24 }25 26 return $fields;27}28 29// Set the browser value to our morphed data.30public function getFormFields($object)31{32 $fields = parent::getFormFields($object);33 34 $linkable = $object->linkable;35 36 if ($linkable) {37 $fields['browsers']['linkables'] = collect([38 [39 'id' => $linkable->id,40 'name' => $linkable->title,41 'edit' => moduleRoute($object->linkable->getTable(), 'content', 'edit', $linkable->id),42 'thumbnail' => $linkable->defaultCmsImage(['w' => 100, 'h' => 100]),43 ],44 ])->toArray();45 }46 47 return $fields;48}
And finally in our form we can add the field:
1 2<x-twill::browser 3 label="Link" 4 :max="1" 5 name="linkables" 6 :modules="[ 7 [ 8 'label' => 'Homepages', 9 'name' => 'homepages',10 ],11 [12 'label' => 'Pages',13 'name' => 'content.pages'14 ]15 ]"16/>