Skip to main content

Fields

The Qore fields are described below in alphabetical order. Relational field can be found here: Relation fields.

Action button

An action button will call the given resource action when the user clicks on it:

    ActionButton::make('Label', 'name')
->deselectOnIndexByDefault()
->forAction($this->action(MyActionClass::class))

Button

You can add a button to your resource pages that runs some PHP code:

Button::make('Buttons!', 'button_1')
->onClick(function (ButtonClickEvent $event) {
dd('button was clicked!');
})

Optionally you can customise it:

->setOutline()
->setColor('primary')
->setTextColor('primary')
->setIconRight('arrow_forward')
->setIconLeft(null)
->setDense(false)
->setCaps(false)
->setSize('md')
->setFlat(false)
->setTo('/resources/users/create') // Internal route url, don't open in new tab
->setHref('https://google.com', true) // External route url, open in new tab
->setFormClassName('q-ma-mx')
->setDetailClassName('bg-negative')
->setIndexClassName('bg-positive')

You can change the response message and display it as error:

->onClick(function (ButtonClickEvent $event) {
$event->getResponse()->setMessage('My message!')->setFailed();
})

To show a confirm prompt:

->withDialog('Hello from confirm dialog!')
->withDialog('This is with a type!', 'error') // 'error'|'positive'|'info'

You can also include fields:

->onClick(function (ButtonClickEvent $event) {
dd($event->getDialogFormState());
})
->withFields(function (ButtonFieldsResponse $response) {
$response->setFields(new FieldCollection(
Text::make('Some', 'text')
->rules('required')
));
})

Other useful functions:

->onClick(function (ButtonClickEvent $event) {
$event->getForm(); // The `ManagesForm` instance if this field exists in a form
$event->getModel(); // The model instance if we have one
})
->withFields(function (ButtonFieldsResponse $response) {
$response->setFieldLayout(...); // You can provide a `FieldLayout` here
$response->getModel(); // The model instance if we have one
$response->getParentState(); // The state from the related (parent) form
$response->getParentMeta(); // The metadata from the related (parent) form

$response->onFormReady(function(ManagesForm $form) {
$form->setSubmitButtonLabel('Hi!'); // Set the submit label when the form is ready
});
})
caution

Parts of the Button are cached. The key for this cache is based on the name of your field (and the resource name if the field exists inside a QoreResource). So be careful not having 2 buttons with the same name, and make sure to refresh your page once to bust the cache.

caution

If the Button field is used in a QoreResource, then the create and update policies are checked by default. However, if your Button exists in a custom form, then you will need to authorise manually in the onClick closure.

info

The onClick and withFields Closures will be stored as a SerializableClosure. If you ever run into unexpected problems, please visit the docs: https://github.com/laravel/serializable-closure

Checkbox

A checkbox is very straight forward:

Checkbox::make(__('Check 1'), 'check_1')

Checkbox::make(__('Check 2'), 'check_2')
->default(true)

Choice

Choice behaves like a radio button:

Choice::make(__('crm::crm.Layout'), 'crm_layout_mode')
->rules('in:tabs,cards', 'required')
->setMaxWidth('md')
->options([
[
'value' => 'tabs',
'label' => __('crm::crm.Tabs'),
'description' => __('crm::crm.Show fields in seperate tabs')
],
[
'value' => 'cards',
'label' => __('crm::crm.Cards'),
'description' => __('crm::crm.Show fields in seperate cards')
]
])

Optionally make it look denser:

Choice::make(__('crm::crm.Layout'), 'crm_layout_mode')
->dense()

Code

Adds a field where users can fill code:

Code::make(__('Content'), 'content')
->setLanguage('twig') // or html, css etc.
->rules('required', 'string'),

Color

Adds a field where users can pick a color:

Color::make(__('Primary color'), 'primary_color')
->default($tenant->getPreference('primary_color'))

CompanyName

Most companies have the same base information: company name, coc number, vat number, address, etc.

The companyName field can be used with the Kvk plugin to auto fill company information

If the Kvk plugin is active you can define the name of your Kvk Field and your Address Field

with the fieldNames method:

CompanyName::make(__('Company name'), 'company_name')
->fieldNames('kvk_number', 'address')
->rules('required'),

The first argument of the fieldnames method should be filled with the name of the Kvk field

The second argument of the method shoud be filled with the name of your Address Field

Country

info

By default users can only select activated countries

The Country field can be used to assign a country to for example a user or employee:

public function fields(): FieldCollection
{
return new FieldCollection(
Country::make(__('Country'), 'country'),
}

Important to know is that the country field extends the BelongsTo field, so your model needs to have the same belongsTo relation to work.

CreatedAt

CreatedAt is a wrapper around DateTime, and cannot be edited

CreatedAt::make()

Currency Select

A currency code is a 3 character based code (e.g.: EUR) and can be selected:

CurrencySelect::make(__('Currency'), 'currency')
->default(preference('currency'))

Date

Adds a date picker:

Date::make(__('Active from'), 'active_from')
->rules('nullable', 'date')

You can optionally show a "Diff for humans" for detail & index instead of showing the actual date:

Date::make(__('Active from'), 'active_from')
->displayUsingDiffForHumans(bool $onShow = true, bool $onIndex = true)

It is possible to set minimum date, maximum date, disabled dates and enabled dates.

You can set a minimum date:

Date::make('Date', 'updated_at')
->minDate(Carbon::now()->subWeek())

// Or:
->minDate(function(?Model $model) {
return Carbon::now()->subWeek();
})

You can set a maximum date:

->maxDate(Carbon::now()->addWeek())
// Or:
->maxDate(fn (?Model $model) => now()->addWeek())

You can also disable a range of dates. Qore accepts an array of Carbon instances, but you can also supply arrays that contain exactly 2 Carbon dates (min and max date):

->disabledDates([Carbon::now()->addDays(1)])
// Or:
->disabledDates(function(?Model $model) {
return [
Carbon::now()->addDays(1),
];
})

// Disable tomorrow, and next week, and next month:
->disabledDates(function(?Model $model) {
return [
Carbon::now()->addDays(1),
[Carbon::now()->addWeeks(1), Carbon::now()->addWeeks(2)],
[Carbon::now()->addMonths(1), Carbon::now()->addMonths(2)],
];
})

You can also specify only enabled dates (syntax same as ->disabledDates)

// Only today is enabled:
->enabledDates([Carbon::today()])

// Every date is disabled, except:
->enabledDates(function(?Model $model) {
return [
Carbon::now()->addDays(1),
[Carbon::now()->addWeeks(1), Carbon::now()->addWeeks(2)],
[Carbon::now()->addMonths(1), Carbon::now()->addMonths(2)],
];
})
caution

Please note that you will have to add validation yourself

DateTime

Adds a datetime picker:

DateTime::make(__('Planned at'), 'planned_at')
->rules('nullable', 'date', 'after_or_equal:now')

You can optionally show a "Diff for humans" for detail & index instead of showing the actual date time:

DateTime::make(__('Planned at'), 'planned_at')
->displayUsingDiffForHumans(bool $onShow = true, bool $onIndex = true)

Furthermore, most Date field features should work on this field too.

DetailButton

You can add a button to you index tables which will open a dialog which contains some information:

DetailsButton::make(__('Details'), 'metadata')
->displayUsing(function($value, Model $model) {
return 'Hello world';
})

You can also use a HTML string:

DetailsButton::make(__('Details'), 'metadata')
->displayUsing(function($value, Model $model) {
return '<h1>Hello world</h1>'
})
->setDialogWidth(1200)
->asHtml(),

Duration

You can add a duration field:

Duration::make(__('Every'), 'duration')

The value of this field will be of type: int.

Customization

You can enable/disable different units by calling the disable function for every unit:

Duration::make(__('Every'), 'duration')
->disableDays()
->disableMinutes(disable: false)
info

By default the seconds & days units are disabled

Setting a default can be done by either sending a CarbonInterval, or by sending the seconds to the field:

Duration::make(__('Every'), 'duration')
->default(100) # 100 seconds
->default(CarbonInterval::minutes(5))

You can change the step of certain units:

Duration::make(__('Every'), 'duration')
->secondsStep(30)
->minutesStep(15)
tip

You can also call ->step() to change the step of all units at the same time

You can change the way the field is displayed in Tables, by calling the prettyPrint function:

Duration::make(__('Every'), 'duration')
->prettyPrint()

Duration::make(__('Every'), 'duration')
->prettyPrint('%d-%H:%i:%s') # or with a custom format
info

This will change the displayUsing option of the field, if you are implementing a custom one you shouldn't use this

Validation

This field represents it's value with seconds, but for simplicity you can call the min & max functions which will automatically convert CarbonIntervals to seconds and append the rule to the list of rules

Duration::make(__('Every'), 'duration')
->min(CarbonInterval::minutes(10))
->max(CarbonInterval::minutes(60))

EncryptedText

Security notice.

Make sure you know:

  • The definition of encryption
  • The difference between encoding, hashing and encryption

When you have a field where you would store sensitive data (for example an authorization key or token), you can use a EncryptedText:

EncryptedText::make(__('API Token'), 'api_token'),

The contents of this field will be encrypted and stored in the database. Upon retrieval, it will be decrypted.

In the front-end the content will be masked until the user manually clicks on the "eye" button.

Hiding the toggle 'show' icon
 ->preventToggle() 
Showing the 'copy to clipboard' icon
 ->showClipboard() 

Without model

If for some reason you use this field without model (for example an extension settings page)

Make sure to decrypt the value before assigning it as default value.

EncryptedText::make(__('nmbrs::settings.auth.key'), 'auth_token')
->default(Illuminate\Support\Facades\Crypt::decrypt($myToken))

Make sure to encrypt the value before saving.

$myEncryptedToken = Illuminate\Support\Facades\Crypt::encrypt($data['auth_token'])

FilePicker

File picker allows file uploads:

use Qore\System\Fields\FilePicker;

FilePicker::make('Contract', 'contract')

You can have specific rules:

->rules('nullable', 'mimes:pdf', 'max:2000'),

Or multiple:

->multiple()

On file fields you can chain the function ->setStorageDisk() which expects the name of the storage disk to store files on different disks. To set the default storage disk u can use the ENV variable MEDIA_DISK which defaults to public.

->setStorageDisk(...)

When having multiple on the filepicker field, you might want to limit the number of files being uploaded. In this case you can add a specific rule on this field:

->rules('max_files:3', 'min_files:1')

Max or min will still work on the size of individual files.

Like the Image field, you need to add the following to your model:

class MyModel extends Model implements HasMedia
{
use InteractsWithMedia;

// ..
}
caution

Make sure you don't have any relationship methods on your model that have the same name as the field name you are using, as it will cause conflicts / unexpected behaviour.

Heading

If you want to have a subtitle in for example your User create / edit form you can do this by defining the Heading field:

info

By default the heading field is hidden on index and show page.

available methods on Heading field:

  • withParagraph() : adds a paragraph under the Heading field
  • withDivider(): adds a separator line under the heading
public function fieldLayout(bool $forDetail, ?Model $model = null): ?FieldLayout
{
return (new FieldLayout())
->column('heading')
}

public function fields(): FieldCollection
{
return new FieldCollection(
Heading::make(__('Heading'), 'heading')
->withParagraph('paragraph')
->withDivider()
->preventFill(),
}

Just like the Paragraph field, ->asHtml() is supported for the paragraph, and you can update the contents of the paragraph dynamically:

Heading::make('Heading', 'heading')
->asHtml()
->withParagraph('Hello world'),

BelongsTo::make(__('Some field'), 'some_name', SomeResource::class)
->setMaxWidth('md')
->onUpdate(function (ManagesForm $form, $id){
$form->setFieldData(
'heading',
'message',
'<strong>MY</strong> new message'
);
}),

Id

Show the ID of the model, only for displaying:

Id::make()
->deselectOnIndexByDefault()

Image

The image field extends the Field class, by default the image field only accepts single image upload:

use Qore\System\Fields\Image;

Image::make('Foto', 'photo');

You can allow the user the paste an external image URL:

Image::make('Foto', 'photo')
->allowFromUrl()

If you want to accept multiple images at the same time you can add the multiple method:

use Qore\System\Fields\Image;

Image::make('Foto', 'photo')
->multiple();

When having multiple on the image field, you might want to limit the number of images being uploaded. In this case you can add a specific rule on this field:

->rules('max_files:3', 'min_files:1')

Max or min will still work on the size of individual images.

The Image field is provided with an image editor so the user can edit the image before upload. The aspect ratio of the image is not set by default, and the user is provided the option to crop the image. if you want to use a fixed aspect ratio you can do so by adding the aspectRatio method:

use Qore\System\Fields\Image;

Image::make('Foto', 'photo')
->multiple()
->aspectRatio(23/9);

By default, the Image field has the edit button to edit images with. If you wish to disable this edit button you can use the hideEditButton method.

use Qore\System\Fields\Image;

Image::make('Foto', 'photo')
->hideEditButton();

You can set the width that an Image will take on either the Index or Detail page by calling the following functions.

Image::make(__('Image'), 'image')
// you can also use 'xs', 'sm', 'md', 'lg', 'xl'
->setDetailMaxWidth(500) // set the width of image (images) on the Detail page
->setIndexMaxWidth(50) // set the width of image (images) on the Index table cell
->setImageMaxWidth(500), // set the width of image (images) on both the Detail & Index page
info

In the case of images (multiple), each individual field will use the width that was set.

Like the FilePicker field, you need to add the following to your model:

class MyModel extends Model implements HasMedia
{
use InteractsWithMedia;

// ..
}
caution

Make sure you don't have any relationship methods on your model that have the same name as the field name you are using, as it will cause conflicts / unexpected behaviour.

info

If you want to edit images in the back-end, please see Other tools

MaterialIconSelect

Lets the user select an icon from the supported Quasar 1 material icons.

use Qore\System\Fields\MaterialIconSelect;

MaterialIconSelect::make(__('qore::system.Icon'), 'icon');

MediaSelect

Similarly to FilePicker field, you can upload files or choose existing media with the MediaSelect field:

MediaSelect::make('Media', 'media')

The media that could be selected is dependent on the permissions of the authorized user. Like the FilePicker field, your model needs to implement the HasMedia trait.

caution

Please note that when you add ->rules('required'), that it will fail on the edit page when the user does not add any new media. In this case it's better to use ->createRules('required'). For the ->editRules you will have access to the model in order to do validation.

Month

The month field makes it possible to select a month and a year:

use Qore\System\Fields\Month;

Month::make('Maand', 'my_date_column')

Number

Adds a numeric input:

Number::make(__('Value'), 'rate')
->rules('required', 'numeric', 'not_in:0')

Form values affix

Numeric form inputs can be prefixed or suffixed.

return Number::make(__('Estimate'), 'estimate')
->withPrefix('$')
->withSuffix('Monthly')
Affixing currency symbols.

Countries present currency symbols differently. withCurrencySymbol() can be used. Where to place this symbol will be determined in the background based on the request user's locale.

return Number::make(__('Estimate'), 'estimate')
->withCurrencySymbol('$')

Decimals

By default, decimals are not allowed, you can however allow them:

Number::make(__('Value'), 'rate')
->allowDecimals() // 2 by default
// or
->allowDecimals(10)

Validation

By default any numeric input is valid, however you can set a valid range for the field by using min, max or between:

    Number::make('estimate', 'estimate')
->min(0)
->max(800)
// or
->between(0, 800);

To add steps to the front-end, use step:

    Number::make('estimate', 'estimate')
->step(0.2);

Paragraph

If you want to display a piece of text or HTML, you can do so by defining the Paragraph field:

info

by default the paragraph field is hidden on index and show page.

Paragraph::make(__('Paragraph'), 'paragraph')

You can also define a type:

Paragraph::make(__('Paragraph'), 'paragraph')
->type('warning')

By default, the label will be used for the text. You can also supply HTML there. In some cases you want the text to be changed dynamically, you can set the data message for the field:


Paragraph::make(__('Warning message'), 'warning')
->setMaxWidth('lg')
->type('warning'),

BelongsTo::make(__('Some field'), 'some_name', SomeResource::class)
->setMaxWidth('md')
->onUpdate(function (ManagesForm $form, $id){
$form->setFieldData(
'warning',
'message',
__('My new message')
);
}),

Phone

The phone field automatically validates and formats the users' input.

The data is stored as json, for example your migration may look like this:

$table->json('phone_1')->nullable();

And in the model:

protected $casts = [
'phone_1' => 'array'
];

By default, the default country is selected. You can modify this:

Phone::make(__('Phone'), 'phone')
->country('NL')

All active countries are selectable in the dropdown. You can change this however.

To make all countries selectable:

Phone::make(__('Phone'), 'phone')
->country('NL')
->selectableCountries('*')

Only make the following countries selectable:

Phone::make(__('Phone'), 'phone')
->country('NL')
->selectableCountries('NL', 'US', 'DE')

Place

info

In order to fully make use of address fields you have to include the postcode and or geocoding plugins.

Please see Other tools for more info

The place field will include fields like zipcode, a house number, an addition, a street and more (based on selected country):

Place::make(__('Bezoek adres'), 'address_1')

Places are stored as json, for example your migration may look like this:

$table->json('address_1')->nullable();

And in the model:

protected $casts = [
'address_1' => 'array'
];

The display value is determined by which country is selected. The full_name attribute of the json value will be used for this. If you wish, you can of course have a different display value:

Place::make(__('Adres'), 'address_1')
->displayUsing(function(array $address) {
return [
'full_name' => $address['zipcode'] . ' ' . $address['province']
];
})

You can enable displaying a map on the detail page:

Place::make(__('Adres'), 'address_1')
->withMap(true)

By default, the default country is selected. All active countries are selectable in the dropdown. You can modify this:

Place::make(__('Adres'), 'address_1')
->country('NL')

Sometimes the customer wants to select a different address based on the state of the form. For example, when an organization is selected, the user may want to switch between addresses. You can achieve this by setting the field data via another field:

    $form->setFieldData(
'address',
'selectable_addresses',
$addresses->map(function (Address $address) use ($addressMapper) {
return [
...$addressMapper($address),
'full_name' => $address->full_name,
'label' => $address->label
];
})->toArray()
);

PreferenceSelect

Wrapper around Select field which will add options for known preferences automatically:

PreferenceSelect::make(__('Date format'), 'date_format')
->default(preference('date_format'))

Price

Wrapper around Number field which allows for decimals and adds valuta symbol:

Price::make(__('invoicing::invoicing.Danger amount'), 'danger_amount')
->rules('numeric', 'not_in:0', 'min:0')

Rating

Wrapper around Number field which implements the QRating component from quasar:

Rating::make(__('Rating'), 'rating') // the field expects the column in the DB to be number

You can use any settings available in the QRating Quasar Component:

// all settings from the Rating component in Quasar can be changed
Rating::make(__('Rating'), 'rating')
->size('md')
->color('primary')
->icon(['settings', 'manufacturing'])
->maxIcons(10) // set the max amount of the field to 10 icons (rule isn't added by default)

RelatedToMany

Wrapper around BelongsToMany field which allows models to relate to eachother, for example, an organization could relate to many other relatives:

RelatedToMany::make(__('Organization Relatives'), 'relatives', $this->resourceClass())
->rules('nullable', 'exists:organizations,id')
->withRelationalDisplay(function (Model $model) {
$title = $model->name;

if ($model->addresses->count() > 0) {
$title .= ', ' . $model->addresses->first()->residence;
}

return $title;
})
->withoutAttaching()
->deselectOnIndexByDefault()

Relation Table

Sometimes you just need to show a custom table with custom columns and data without needing another resource.

You can achieve this:

RelationTableField::make('Hobbies', 'hobbies')
->columns(new ColumnCollection(
Text::make('Name', 'name'),
))
// Use the model
->query(function (?Model $employee) {
return $employee->hobbies()->practicing();
})
// or a query
->query(\App\Models\Tenant\Hobbies::query())

Repeater

The Repeater field makes it possible to repeat fields in a Form and allows the user to add and delete rows.

A basic example:

Repeater::make('My repeated', 'my_column')
->fields(new FieldCollection(
Text::make('Hello repeater!', 'repeat')
->setRepeatedWidth(200) // The width of this field
->default('Hello world')
->rules('required', 'min:3'),
Checkbox::make('Check!', 'check')
))

By default, the value (array) is stored as json. In this case, the data will be stored in the my_column column. This means that you need a cast on your model:

    protected $casts = [
'my_column' => 'array'
];

Often though, you will probably not want to save the value. In that case you can add fillLazyUsing on the field:

Repeater::make('My repeated', 'my_column')
->fillLazyUsing(function(Model $model, array $data) {
// dd($data)
})

The Repeater is quite large by default, you can set it to dense. You can also set the layout to vertical.

Repeater::make('My repeated', 'my_column')
->dense()
->vertical() // 1 field per row

The Repeater field holds an array as its value inside a form. If you would like to set the state of this field, you will have to pass an array that also includes a uuid. In the following example a Button field will set the state for the Repeater field:

Repeater::make('My repeated', 'my_column')
->fields(new FieldCollection(
Text::make('Hello repeater!', 'repeat'),

Button::make('Button support!', 'button')
->hideOnShow()
->onClick(function(ButtonClickEvent $event) {
if ($event->getForm()) {
$rowIndex = $event->getMetadata('row_index');
$dialogState = $event->getDialogFormState();

$currentState = $event->getForm()->getState('my_column');
$currentState[$rowIndex]['repeat'] = $dialogState['hello'];

$event->getForm()->setState('my_column', $currentState);
}
})
->withFields(function(ButtonFieldsResponse $response) {
$response->setFields(new FieldCollection(
Text::make('Hello', 'hello')
));
})
)),
caution

Please note that some functionality is limited or disabled for repeatable fields. Only a handful of fields are now repeatable ( like Text, Textarea, Select, Button, Checkbox, BelongsTo, BelongsToMany, Date, DateTime), however most fields can be made repeatable easily.

info

If you want to create your own Repeatable field, or make an existing field repeatable, you need to add the following interface to your field: FieldIsRepeatable. You can then optionally override methods in your field if necessary (BelongsToMany example):

public function getRepeatedFormValue(mixed $value, Model $model): mixed
{
if ($value) {
return json_decode($value);
}

return [];
}

public function getRepeatedDisplayValue(mixed $value, Model $model): mixed
{
if (is_null($value)) {
return null;
}

$value = $this->otherResource->model()::whereIn('id', json_decode($value))->get();

return $value->map(function (Model $model) {
return array_merge([
'id' => $model->id,
'url' => $this->otherResource->url($model->id),
'title' => $this->otherResource->modelTitle($model)
], $this->otherResource->toHttpResource($model)->toArray(request()));
});
}

In your Field.vue in the front-end, you will receive 3 props: isRepeated, repeatedIndex and dense. Based on these props you can adjust your field to work well as a repeatable.

When the field is cleared, and you want Qore to add at least one row, you can add the following:

->notEmptyOnNull()

You can set the maximum amount of rows:

->max(5)

You can disallow deleting rows:

->deletable(false)

Metadata in repeater

You can set the field meta for a repeater field using dot notation. Keep in mind that this meta will be the same for each row.

Repeater::make('My repeater', 'my_repeater')
->fields(new FieldCollection(
Text::make('Text 1', 'text_1'),
BetterTime::make('Time', 'time')
->withMinuteInterval(30)
->withoutDropdown()
)),

Text::make('Name', 'name')
->onUpdate(function(ManagesForm $form) {

$form->setFieldMeta('my_repeater', 'fields.time.minuteInterval', 5);
})

Repeater sort order

You can allow the user to sort the rows in the repeater field by calling the draggable method:

Repeater::make('My repeater', 'my_repeater')->draggable()

Select

Select works like a dropdown:

Select::make(__('Type'), 'type')
->options([
[
'value' => 'container',
'label' => 'Container'
]
])
->default('container')
->rules('required', 'in:container')

Optionally, you can also supply a description, group, or disable:

Select::make(__('Type'), 'type')
->options([
[
'value' => '1',
'label' => 'Option 1',
'description' => 'My description'
],
[
'group' => 'My group',
'disable' => true,
],
[
'value' => '2',
'label' => 'Option 2',
'disable' => true
],
])

If you want to change the options based on the state of the form:

    $form->setFieldData(
'type',
'options',
[...]
);

Select Enums

Instead of supplying options, you can also pass in an Enum:

Select::make('Ticket category', 'category')
->forEnum(TicketType::class)
->setColumnStyle(function (TableDataStyler $styler, Model $model) {
if ($model->category === 'bug') {
$styler->setRowClass('bg-red-1');
}
})

By default, it will use all enum cases as options. Qore will check if the following methods exists on your enum: label, description, options. Your Enum may look like this:

<?php

namespace App\Enums;

enum TicketType: string
{
case SUPPORT = 'support';
case BUG = 'bug';
case QUESTION = 'question';

// Label per case (optional but recommended)
public function label(): string
{
return match($this) {
self::SUPPORT => __('Support ticket'),
self::BUG => __('Support bug'),
self::QUESTION => __('Support question'),
};
}

// Description per case (optional)
public function description(): string
{
return match($this) {
self::SUPPORT => __('Description for Support'),
self::BUG => __('Description for Bug'),
self::QUESTION => __('Description for Question'),
};
}

/* Override the options manually (optional)
public static function options(): array
{
return [
[
'label' => 'Bug',
'value' => 'bug'
],
[
'label' => 'Question',
'value' => 'question'
],
];
}
*/
}

Select Validation

When supplying options, by default validation will be added from the field. The rule In will include all option values. You can disable this by supplying false in the last parameter:

public function options(
array $options,
string $valueKey = 'value',
string $labelKey = 'label',
bool $addRule = true
)
// Or when working with Enum:
public function forEnum(string $enumClass, bool $addRule = true)

Separator

info

by default the separator field is hidden on index and show page.

The Separator field can be used to add a separator between your fields:

public function fieldLayout(bool $forDetail, ?Model $model = null): ?FieldLayout
{
return (new FieldLayout())
->column('separator')
}

public function fields(): FieldCollection
{
return new FieldCollection(
Separator::make(__('Separator'), 'separator')
->preventFill(),
}

Slider

The Slider class extends the Number class, providing a specialized component for creating slider input fields with additional customization options. The main features of the Slider class include:

  • Snap to Steps: The snap method enables the slider to snap to defined steps.
  • Display Current Value Label: The label method allows the slider to display a label with the current value.
  • Display Markers: The markers method adds markers along the slider track.
  • Vertical Orientation: The vertical method switches the slider to a vertical orientation.
  • Reverse Orientation: The reverse method reverses the direction of the slider.
  • Always Show Label: The labelAlways method ensures the label with the current value is always visible.
// Enables snap to steps
$field->snap():
// Displays a label with the current value
$field->label():
// Displays markers along the slider
$field->markers():
// Displays the slider vertically
$field->vertical():
// Reverses the slider direction
$field->reverse():
// Always shows the label with the current value
$field->labelAlways():

In addition to these methods, the methods from the Number field are also available for use

Set min
$field->min(int):
$field->max(int):
$field->between(int, int):
$field->step(int):

SerialNumber

This field will automatically generate & show a serial number for your resource. The name of the field should match one of your database serial numbers.

SerialNumber::make(__('Serial number'), 'serial_number', 'organizations')

SimpleEditor

When you need textarea, but with a few functionalities like making some text bold, you can use the SimpleEditor field. This is a lightweight WYSIWYG editor:

SimpleEditor::make(__('My description'), 'description')

SettingSelect

Wrapper around Select field which corresponds to config/components.php:

SettingSelect::make(__('2FA required for everyone'), '2fa_required')
->forComponent('authentication')
->hideClearButton()
->default(setting('authentication', '2fa_required'))

Status

Wrapper around Select field which shows a color per option:

Status::make(__('mailing::mailing.Status'), 'status')
->setColumnWidth(200)
->statuses([
[
'value' => 'error',
'label' => __('mailing::mailing.Error'),
'color' => 'negative',
],
[
'value' => 'pending',
'label' => __('mailing::mailing.Pending'),
'color' => 'grey-9',
],
[
'value' => 'planned',
'label' => __('mailing::mailing.Planned'),
'color' => 'primary',
],
[
'value' => 'queued',
'label' => __('mailing::mailing.Queued'),
'color' => 'blue-6',
],
[
'value' => 'sent',
'label' => __('mailing::mailing.Sent'),
'color' => 'positive',
]
])

Text

Simple text field:

Text::make(__('Name'), 'name')
->default(auth()->user()->name)
->setMaxWidth('sm')

Optionally set attributes:

->withAttributes([
'type' => 'email'
])

Textarea

Simple textarea field:

Textarea::make('Omschrijving', 'description')

Time

For the Time field you will need a string column in the database.

Time::make(__('Arrival time'), 'arrival_time')
->rules('required')

You can also force time intervals, for example only quarters like 10:00, 10:15 etc.:

Time::make(__('Arrival time'), 'arrival_time')
->minuteOptions(Time::$FIFTEEN_MINUTES_INTERVAL)
->rules('required')

You can choose to use a 24-hour format:

Time::make(__('Arrival time'), 'arrival_time')
->use24hFormat()

BetterTime

For the BetterTime field you will need a string column in the database.

BetterTime::make(__('Start time'), 'start_time')
->rules('required')
Configuration

The BetterTime field uses vue2-timepicker which comes with comprehensive configuration.
The field currently supports a small subset of these configurations:

  • minuteInterval(int $interval, bool $validate = true)

Sets the minuteInterval for the field. If set to 5, the user will be able to select [5, 10, ... 50, 55]. If validate is set to true a validation rule will be applied to check the minute interval.

  • setHourRange/setMinuteRange(array $range, bool $validate = true)

Sets the range for hours/minutes that a user can choose from. When [5, 10, [20, 30]] is used the user will be able to pick from [5,6,7,8,9,10,20,21,22 ... 28, 29, 30] If validate is set to true a validation rule will be applied to check if the submitted hour/minute is in the given range.

  • hideDisabledItems(bool $hide)

Hides the disabled items, for example when using minuteInterval = 5, minutes 1, 2, 3, 4, 6, ... will be hidden. When using a range, values outside this range will be hidden.

TwigTemplate

Wrapper around Wysiwyg, but adds twig tag support:

TwigTemplate::make(__('Content'), 'content')
->rules('required', 'string')

UpdatedAt

UpdatedAt is a wrapper around DateTime, and cannot be edited

UpdatedAt::make()

VariableValue

Wrapper around BelongsTo field, which uses the tenant_variable_values table:

VariableValue::make(__('Type'), 'type')
->forVariable(tenant_variable('address types'))
->hideClearButton()

VatNumber

If you want to validate the VatNumber (VAT) of a company you can use the VatNumber field. The VatNumber field validates the filled in number and will return if the number is valid or not.

If you want to check that the company name is the same as the company name of the checked VatNumber number, you can achieve that by using the setCompanyField method:

VatNumber::make(__('VatNumber number'), 'btw_number')
->setCompanyField('company_name')
->rules('required')

Now the VatNumber field will show visually a warning if the names are not equal.

Validation

The VatNumber field uses the VatValidator client to validate.

The VatValidator can be used outside of the field, by calling it statically.

It has 2 methods:

validateVatNumber()

This function will use the validateVatFormat and combine it with the getVatInfo.

Besides checking for the format, it will also query the EU API to check wether that VAT is valid.

info

When setting the setCompanyField on the VAT Number, the company value from getVatInfo will be used for validation.

VatValidator::validateVatNumber('NL854564354B01'); //return boolean true (VAT format valid & it exists)
VatValidator::validateVatNumber('NL854564354B02'); //return boolean false (VAT format valid & VAT doesn't exist)

validateVatFormat()

This function cheks the format of a VAT. It will use regex to check if the string matches the specified countries format.

VatValidator::validateVatFormat('RO999999999'); //return boolean true
VatValidator::validateVatFormat('RX00000000000'); //return boolean false

getVatInfo()

This function retrieves data from the EU API about a certain VAT number. The following data is retrieved:

VatValidator::getVatInfo('NL854564354B01');

// returns the following array:
[
'countryCode' => string
'vatNumber' => string,
'valid' => bool,
'name' => string, // company name
'address' => string
]

Website

The website field will add http(s):// and check the validity of an url:

Website::make(__('Website'), 'website')

If you only want to accept websites that are online at the moment (websites that return a 200 response):

Website::make(__('Website'), 'website')
->demandOnlineWebsite()

Wysiwyg

if you want to use a what you see is what you get editor you can use the Wysiwyg field. By default you get a very minimal wysiwyg editor, you can change this by overwriting the advanced method:

public function fields(): FieldCollection
{
return new FieldCollection(
Wysiwyg::make('wysiwyg', 'wysiwyg')
->advanced(),
}

You can also set some default options:

public function fields(): FieldCollection
{
return new FieldCollection(
Wysiwyg::make('wysiwyg', 'wysiwyg')
->defaultHeight(400)
->defaultFontFamily('Tahoma')
->defaultFontSize(12)
}

The field also supports variables:

->withTwig()
->withVariables([
'Variable group 1' => [
'organization.name',
'organization.number'
],
'Variable group 2' => [
'organization.city',
'organization.country_name'
]
]);
caution

the content of the field can be very long so make sure in your migration you set the field type to longtext

public function up()
{
Schema::create('wysiwyg', function (Blueprint $table) {
$table->longText('wysiwyg')->nullable();
});
}

Purify

Content will be purified by default (remove potential malicious code). You can override or disable purifying by using the purifyUsing method:

Wysiwyg::make('Content', 'my_editor')
->purifyUsing(function(Model $model, ?string $content) {
return $content;
})

Addresses

See documentation

PhoneNumbers

See documentation

EmailAddresses

See documentation