Basics

You're browsing the documentation for an old version of MoonShine. Consider upgrading your project to MoonShine 2.x.

Fields is one of the most important sections along with resources. We have already discussed the process of Fields registration in the Resource section, but let's figure out how to customize them to your needs! The fluent interface is used for convenience

# Make

First of all, let's understand how the make method works when creating an instance of a field

Text::make(string $label = null, string $field = null, ResourceContract|string|null $resource = null)

$label - Label, field header
$field - A field in the database (e.g. name) or a relation (e.g. countries)
$resource - If $field is a relation, then in this parameter you need to specify a field in the linked table that will be displayed in the view

$resource can also be a Resource class. In this case, if the $titleField property is specified, the field of the relation will be defined through it

//...
class MoonShineUserResource extends Resource
{
public static string $model = MoonshineUser::class;
 
public static string $title = 'Administrators';
 
public string $titleField = 'name';
//...

# Displaying

The fields are displayed at the page (the main page of the resource) and at the create/edit page. To exclude the field from the main page or the page with the form, you can use hideOnIndex/hideOnForm/hideOnDetail methods and showOnIndex/showOnForm/showOnDetail reverse methods. To exclude it from the edit or add page only - use hideOnCreate/hideOnUpdate/showOnCreate/showOnUpdate

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
ID::make(),
Text::make('Title', 'title')
 
->hideOnIndex()
->hideOnForm()
 
,
])
];
}
 
//...

# Conditional display

This method also accepts bool, or Closure

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
ID::make(),
Text::make('Title', 'title')
 
->hideOnIndex(auth()->check())
 
,
])
];
}
 
//...

# Attributes

As the form renders the html element, you can use basic html attributes as well. Such as disabled, autocomplete, readonly, multiple etc.

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
Text::make('Title', 'title')
->disabled()
->hidden()
->readonly(),
];
])
}
 
//...

# Arbitrary attributes

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
Password::make('Password', 'password')
->customAttributes(['autocomplete' => 'off'])
])
];
}
 
//...

# Required field

To make the field mandatory, you must use the required method

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
Text::make('Title', 'title')
->required()
])
];
}
 
//...

# Dynamic value

//...
 
public function fields(): array
{
return [
Block::make('Block title', [
Text::make('Name', 'first_name', fn($item) => $item->first_name . ' ' . $item->last_name)
 
// Example if you want to separate the logic for the main and for editing
Text::make('Price', resource: function ($item) {
if(request()->routeIs('*.index')) {
return $item->price;
}
 
return $item->exists ? $item->price->raw() : 0;
}),
])
];
}
 
//...

# Hint

You can add a hint with a description to the field by calling the hint

//...
 
public function fields(): array
{
return [
Number::make('Rating')
->hint('From 0 to 5')
->min(0)
->max(5)
->stars()
];
}
 
//...

You can add a link to the field (e.g. with instructions) addLink(string $name, string $link, bool $blank = false)

//...
 
public function fields(): array
{
return [
Text::make('Title', 'title')
->addLink('YouTube', 'https://youtube.com')
// or with the anonymous function
->addLink('Test', function() {
if(!$this->getItem()) {
return route('admin.brands.index');
}
 
return route('admin.brands.edit', $this->getItem()->brand_id);
}),
];
}
 
//...

# Nullable

If you want to save NULL nullable() by default

# Sorting

To be able to sort the field on the main page of the resource, you must add the sortable method

//...
 
public function fields(): array
{
return [
Text::make('Title', 'title')
->sortable()
];
}
 
//...

# Hide label

The fieldContainer method hides Label fields to save space, especially useful in conjunction with the Flex decoration

//...
 
public function fields(): array
{
return [
Text::make('Title', 'title')
->fieldContainer(false)
];
}
 
//...

# Default value

Use the default method if you want to specify a default value for the field

//...
 
public function fields(): array
{
return [
Text::make('Title', 'title')
->default('-')
];
}
 
//...

# Display condition

There may be a need to display a field only if the value of another field in the form has a certain value (For example: display the phone only if there is a check mark for it). For these purposes, the showWhen($column, $operator, $value) method is used

Available operators:

= < > <= >= != in not in

If the operator is not specified, then = will be used

//...
 
public function fields(): array
{
return [
Phone::make('Phone', 'phone')
->showWhen('has_phone','=', 1)
// или
Phone::make('Phone', 'phone')
->showWhen('has_phone', 1)
];
}
 
//...

If the statement is in or not in, then in $value you need to pass an array

//...
 
public function fields(): array
{
return [
Select::make('List', 'list')->multiple()->options([
'value 1' => 'Option Label 1',
'value 2' => 'Option Label 2',
'value 3' => 'Option Label 3',
]),
 
Text::make('Name', 'name')
->showWhen('list', 'not in', ['value 1', 'value 3']),
 
Textarea::make('Content', 'content')
->showWhen('list', 'in', ['value 2', 'value 3'])
];
}
 
//...

# Ability to save

//...
 
public function fields(): array
{
return [
Text::make('Title', 'title')
->canSave(false)
// or
->canSave(fn() => false)
];
}
 
//...

# Events

When writing your own Fields, you may need to interact with events before and after saving. To do this, you have to implement the relevant methods in your custom field

public function beforeSave(Model $item): void
{
//
}
 
public function afterSave(Model $item): void
{
//
}

# Change view

Sometimes it makes sense to change the view using a fluent interface (For example, if you use filters or fields outside of MoonShine)

Text::make('Title')
->customView('fields.my-custom-input'),

# Methods by condition

The when method implements the fluent interface and will execute a callback when the first argument, passed to the method is true.

Text::make('Slug')
->when(isset($this->getItem()->id), fn(Text $field) => $field->locked()),

The field instance will be passed to the callback function.

The second callback can be passed to the when method, it will be executed, when the first argument passed to the method has a false value.

Text::make('Slug')
->when(
isset($this->getItem()->id),
fn(Text $field) => $field->locked(),
fn(Text $field) => $field->hidden()
),

The unless method is the reverse of the when method and will execute the first callback, when the first argument has a false value, otherwise the second callback will be executed if it was passed to the method.

Text::make('Slug')
->unless(
auth('moonshine')->user()->moonshine_user_role_id === 1,
fn(Text $field) => $field->readonly()->hideOnCreate(),
fn(Text $field) => $field->locked()
),