In Twill you can customize the module tables and browser tables to your own need.
To modify the table you start by overwriting the getIndexTableColumns(): TableColumns
method or
the getBrowserTableColumns(): TableColumns
method for browser lists.
You can manage this in 2 ways, but the easiest is to self define the columns without
calling parent::getIndexTableColumns
.
To define columns start by instantiating a TableColumns
object:
1use A17\Twill\Services\Listings\TableColumns;2 3protected function getIndexTableColumns(): TableColumns4{5 $columns = new TableColumns();6}
Then add as many fields as you need.
1use A17\Twill\Services\Listings\Columns\Text; 2use A17\Twill\Services\Listings\TableColumns; 3 4protected function getIndexTableColumns(): TableColumns 5{ 6 $columns = new TableColumns(); 7 8 $columns->add( 9 Text::make()10 ->field('title')11 ->title(twillTrans('Title'))12 );13 14 ...15}
In some cases you might want to just add some columns to your table. For this you can override the following methods:
1/** 2 * Similar to @see getBrowserTableColumns but these will be added on top of the default columns. 3 */ 4protected function additionalBrowserTableColumns(): TableColumns 5{ 6 return new TableColumns(); 7} 8 9/**10 * Similar to @see getIndexTableColumns but these will be added on top of the default columns.11 */12protected function additionalIndexTableColumns(): TableColumns13{14 return new TableColumns();15}
For additionalIndexTableColumns
these items will be added after the title.
In additionalBrowserTableColumns
these will be added at the end.
Using collection methods, you can easily add a thumbnail before the title column in the table:
1protected function getIndexTableColumns(): TableColumns 2{ 3 $table = parent::getIndexTableColumns(); 4 5 $after = $table->splice(1); 6 7 $table->push( 8 Image::make()->field('cover') 9 );10 11 return $table->merge($after);12}
There are currently 10 different columns. Most column share a set of setters that you can use. Because these columns are regular php objects, you can always use your editor's autocomplete function to discover.
All columns are placed under the src/Services/Listings/Columns
directory.
field(string $field)
: Sets the field in the model where the data should be fetched. This is usually mandatory but in
some special columns it is not.title(?string $title)
: The title to use, if none is provided a Str::title
version of field
will be used.sortable(bool $sortable = true)
: Makes a column sortable.optional(bool $optional = true)
: Makes a column optional.hide(bool $hide = true)
: Hide the field by default. Only usable with optional.linkToEdit(bool $linkToEdit = true)
: Link the field to the edit form.sortKey(?string $sortkey = null)
: The key to use when sorting.renderHtml(bool $html = true)
: If the cms should render the contents as html. (Be careful when using this with
unprotected data sources.)customRender(Closure $renderFunction)
: A closure with a custom render function instead of using the raw field value.linkCell(Closure|string $link)
: A closure or string on where to link the field contents to.shrink(bool $shrink = true)
: Make the column shrink.CustomRender can be useful if you want more control over how you want to render a certain column.
The example blow illustrates a possible usage:
1Text::make()2 ->field('customField')3 ->customRender(function (Model $model) {4 return view('my.view', ['title' => $model->title])->render();5 });
Below is a list of columns and their function:
Text::make()->...
Renders the (translated)value from the model
Boolean::make()->...
Renders a ✅ or ❌ if the field
is true or false.
Image:::make()->
By default this renders the first role media in the model. You can specify the role
and crop
using their same named
methods.
If you add rounded()
the image will be round.
Check the Image
column class for more options.
PublishStatus:::make()->
This field requires no additional methods, it shows on what dates the content will be published and when it will be unpublished.
ScheduledStatus:::make()->
This field requires no additional methods, it shows on what dates the content will be published and when it will be unpublished.
NestedData::make()->...
Renders the field
using the relationship of the same name. It shows information and a link about the nested model.
Languages::make()
This field requires no additional methods, it will render the languages the content is available in.
Relation::make()->...
Renders the field
of a relation
column.
For this column type both the relation and field should be provided. If one is missing an exception will be thrown.
Browser::make()->...
Renders the field
of a browser
column.
For this column type both the browser and field should be provided. If one is missing an exception will be thrown.
Presenter::make()
Renders a field using its presenter.
Presenters are currently undocumented and are here for backward compatability. If you want to customize the output of
a column you can use the customRender
method.
If you need you can easily define custom columns in your project by creating a class extending the TableColumn
.
A search field will by default be shown on the top right of the listing. It will by default search the title
of your
module, but you can extend or change that.
You can add setSearchColumns
to your setUpController
method like this:
1namespace App\Http\Controllers\Twill; 2 3use A17\Twill\Http\Controllers\Admin\ModuleController; 4 5class ProjectController extends BaseModuleController 6{ 7 ... 8 9 public function setUpController(): void10 {11 $this->setSearchColumns(['title', 'year']);12 }13}
There are 2 types of filters that can be applied to a table.
Quick filters and filters work together. They are not "or".
By default every module comes with the following quick filters:
To extend or modify this list we have to extend or overwrite the quickFilters
method in the module controller.
1use A17\Twill\Services\Listings\Filters\QuickFilters;2 3public function quickFilters(): QuickFilters;
QuickFilters
is a basic Laravel collection, this means you can do all known manipulations on that list.
You can get the default set of filters by calling:
1public function quickFilters(): QuickFilters2{3 return $this->getDefaultQuickFilters();4}
Then to, for example, add a new filter on top of that you can do the following:
1public function quickFilters(): QuickFilters 2{ 3 $filters = $this->getDefaultQuickFilters(); 4 $filters->add( 5 QuickFilter::make() 6 ->queryString('test') 7 ->label('Test only') 8 ->amount(fn() => $this->repository->whereTranslation('title', 'test')->count()) 9 ->apply(fn(Builder $builder) => $builder->whereTranslation('title', 'test'))10 );11 12 return $filters;13}
A quick filter, as shown above has a few methods that can/need to be called:
queryString
: The unique query string that will be used in the url.label
: The label to show in the filter, will be a "titled" version of queryString if not provided.apply
: A closure with the Builder as parameter, this will by applied to the query of the list when the filter is
active.amount
: A closure that should return the amount of matches. Can be left out to show no number.onlyEnableWhen
: A boolean indicating when the filter should be shown. By default the filter will always be visible.disable
: Disables the filter.If you want to use non of the existing filters but only apply your own you can create a new
instance return Quickfilters::make(...);
.
To further enhance user experience you can add more filters that can be used to refine the results.
Currently filters are only available as select lists. In the future we might extend on these.
By default no filters will be applied to you module listing.
In order to get started you will have to add the filters
method to your module controller:
1use A17\Twill\Services\Listings\Filters\TableFilters;2 3public function filters(): TableFilters;
The same as with quick filters, the TableFilters
object is an extension of a Laravel collection.
Example:
1public function filters(): TableFilters 2{ 3 return TableFilters::make([ 4 BelongsToFilter::make()->field('partner'), 5 FieldSelectFilter::make()->field('year'), 6 BooleanFilter::make()->field('published')->label('Published'), 7 BasicFilter::make() 8 ->queryString('verified') 9 ->options(collect(['yes' => 'Verified', 'no' => 'Not verified']))10 ->apply(function (Builder $builder, string $value) {11 if ($value === 'yes') {12 $builder->where('is_verified', true);13 } elseif ($value === 'no') {14 $builder->where('is_verified', false);15 }16 }),17 ]);18}
Filter methods:
label
: The label to show in the filter, will be a "titled" version of queryString if not provided.queryString
: The unique query string that will be used in the url.options
: A collection of key/value's that will be shown as selectable options.apply
: The callback to apply the filter.default
: Sets the default value of the filter.withoutIncludeAll
: To remove the all
option.onlyEnableWhen
: A boolean indicating when the filter should be shown. By default the filter will always be visible.disable
: Disables the filter.There are a few filters that you can use:
Below is an example of a basic filter, you can use this to have full control over the values and queries.
1BasicFilter::make() 2 ->queryString('verified') 3 ->options(collect(['yes' => 'Verified', 'no' => 'Not verified'])) 4 ->apply(function (Builder $builder, string $value) { 5 if ($value === 'yes') { 6 $builder->where('is_verified', true); 7 } elseif ($value === 'no') { 8 $builder->where('is_verified', false); 9 }10 });
The BooleanFilter
can be used for simple true/false filters.
1BooleanFilter::make()->field('published')->label('Published');
This field will collect the available options from the list in the database.
It can be useful for example to make a select of "years" that a model can belong to.
1FieldSelectFilter::make()->field('year');
A fieldSelectFilter can also add a Without value
option. This is disabled by default but you can enable it by calling
->withWithoutValueOption()
on the filter.
The BelongsToFilter
will make a select with all titles of a relation.
1BelongsToFilter::make()->field('partner');
Additional parameters can be added, but Twill will try to figure these out for you if not provided:
model
: The model that should be used, for example Partner::class
.valueLabelField
: The field name that should be used for displaying the item label. Example: title