MoonShine provides the ability to configure CRUD
pages.
To do this, you need to choose the resource type Model resource with pages
when creating a resource via the command.
This will create a model resource class and additional classes for the index, detail view, and form pages.
The page classes will, by default, be located in the app/MoonShine/Pages
directory.
In the created model resource, CRUD
pages will be registered in the pages()
method.
namespace App\MoonShine\Resources;
use App\MoonShine\Pages\Post\PostIndexPage;
use App\MoonShine\Pages\Post\PostFormPage;
use App\MoonShine\Pages\Post\PostDetailPage;
use MoonShine\Laravel\Resources\ModelResource;
class PostResource extends ModelResource
{
protected function pages(): array
{
return [
PostIndexPage::class,
PostFormPage::class,
PostDetailPage::class,
];
}
}
namespaces
namespace App\MoonShine\Resources;
use App\MoonShine\Pages\Post\PostIndexPage;
use App\MoonShine\Pages\Post\PostFormPage;
use App\MoonShine\Pages\Post\PostDetailPage;
use MoonShine\Laravel\Resources\ModelResource;
class PostResource extends ModelResource
{
// ...
protected function pages(): array
{
return [
PostIndexPage::class,
PostFormPage::class,
PostDetailPage::class,
];
}
}
namespace App\MoonShine\Resources;
use App\MoonShine\Pages\Post\PostIndexPage;
use App\MoonShine\Pages\Post\PostFormPage;
use App\MoonShine\Pages\Post\PostDetailPage;
use MoonShine\Laravel\Resources\ModelResource;
class PostResource extends ModelResource
{
// ...
protected function pages(): array
{
return [
PostIndexPage::class,
PostFormPage::class,
PostDetailPage::class,
];
}
}
namespaces
namespace App\MoonShine\Resources;
use App\MoonShine\Pages\Post\PostIndexPage;
use App\MoonShine\Pages\Post\PostFormPage;
use App\MoonShine\Pages\Post\PostDetailPage;
use MoonShine\Laravel\Resources\ModelResource;
class PostResource extends ModelResource
{
// ...
protected function pages(): array
{
return [
PostIndexPage::class,
PostFormPage::class,
PostDetailPage::class,
];
}
}
namespace App\MoonShine\Resources;
use App\MoonShine\Pages\Post\PostIndexPage;
use App\MoonShine\Pages\Post\PostFormPage;
use App\MoonShine\Pages\Post\PostDetailPage;
use MoonShine\Laravel\Resources\ModelResource;
class PostResource extends ModelResource
{
// ...
protected function pages(): array
{
return [
PostIndexPage::class,
PostFormPage::class,
PostDetailPage::class,
];
}
}
To specify the page type in ModelResource
, the enum
class PageType
is used.
use MoonShine\Support\Enums\PageType;
PageType::INDEX; PageType::FORM; PageType::DETAIL;
namespaces
use MoonShine\Support\Enums\PageType;
PageType::INDEX; // Index page
PageType::FORM; // Form page
PageType::DETAIL; // Detail page
use MoonShine\Support\Enums\PageType;
PageType::INDEX; // Index page
PageType::FORM; // Form page
PageType::DETAIL; // Detail page
namespaces
use MoonShine\Support\Enums\PageType;
PageType::INDEX; // Index page
PageType::FORM; // Form page
PageType::DETAIL; // Detail page
use MoonShine\Support\Enums\PageType;
PageType::INDEX; // Index page
PageType::FORM; // Form page
PageType::DETAIL; // Detail page
Fields in MoonShine are used not only for data input but also for output.
The fields()
method in the CRUD
page class allows you to specify the necessary fields.
namespace App\MoonShine\Pages\Post;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Fields\ID;
use MoonShine\UI\Fields\Text;
class PostIndexPage extends IndexPage
{
protected function fields(): iterable
{
return [
ID::make(),
Text::make('Title'),
];
}
}
namespaces
namespace App\MoonShine\Pages\Post;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Fields\ID;
use MoonShine\UI\Fields\Text;
class PostIndexPage extends IndexPage
{
// ...
protected function fields(): iterable
{
return [
ID::make(),
Text::make('Title'),
];
}
}
namespace App\MoonShine\Pages\Post;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Fields\ID;
use MoonShine\UI\Fields\Text;
class PostIndexPage extends IndexPage
{
// ...
protected function fields(): iterable
{
return [
ID::make(),
Text::make('Title'),
];
}
}
namespaces
namespace App\MoonShine\Pages\Post;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Fields\ID;
use MoonShine\UI\Fields\Text;
class PostIndexPage extends IndexPage
{
// ...
protected function fields(): iterable
{
return [
ID::make(),
Text::make('Title'),
];
}
}
namespace App\MoonShine\Pages\Post;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Fields\ID;
use MoonShine\UI\Fields\Text;
class PostIndexPage extends IndexPage
{
// ...
protected function fields(): iterable
{
return [
ID::make(),
Text::make('Title'),
];
}
}
In MoonShine, you can quickly change the main component on the page.
IndexPage
The getItemsComponent()
method allows you to change the main component of the index page.
getItemsComponent(iterable $items, Fields $fields): ComponentContract
getItemsComponent(iterable $items, Fields $fields): ComponentContract
getItemsComponent(iterable $items, Fields $fields): ComponentContract
getItemsComponent(iterable $items, Fields $fields): ComponentContract
getItemsComponent(iterable $items, Fields $fields): ComponentContract
$items
- field values,
$fields
- fields.
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\TableBuilderContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleIndexPage extends IndexPage
{
protected function getItemsComponent(iterable $items, Fields $fields): ComponentContract
{
return TableBuilder::make(items: $items)
->name($this->getListComponentName())
->fields($fields)
->cast($this->getResource()->getCaster())
->withNotFound()
->when(
! is_null($head = $this->getResource()->getHeadRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->headRows($head)
)
->when(
! is_null($body = $this->getResource()->getRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->rows($body)
)
->when(
! is_null($foot = $this->getResource()->getFootRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->footRows($foot)
)
->when(
! is_null($this->getResource()->getTrAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->trAttributes(
$this->getResource()->getTrAttributes()
)
)
->when(
! is_null($this->getResource()->getTdAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->tdAttributes(
$this->getResource()->getTdAttributes()
)
)
->buttons($this->getResource()->getIndexButtons())
->clickAction($this->getResource()->getClickAction())
->when($this->getResource()->isAsync(), static function (TableBuilderContract $table): void {
$table->async()->pushState();
})
->when($this->getResource()->isStickyTable(), function (TableBuilderContract $table): void {
$table->sticky();
})
->when($this->getResource()->isColumnSelection(), function (TableBuilderContract $table): void {
$table->columnSelection();
});
}
}
namespaces
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\TableBuilderContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleIndexPage extends IndexPage
{
// ...
protected function getItemsComponent(iterable $items, Fields $fields): ComponentContract
{
return TableBuilder::make(items: $items)
->name($this->getListComponentName())
->fields($fields)
->cast($this->getResource()->getCaster())
->withNotFound()
->when(
! is_null($head = $this->getResource()->getHeadRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->headRows($head)
)
->when(
! is_null($body = $this->getResource()->getRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->rows($body)
)
->when(
! is_null($foot = $this->getResource()->getFootRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->footRows($foot)
)
->when(
! is_null($this->getResource()->getTrAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->trAttributes(
$this->getResource()->getTrAttributes()
)
)
->when(
! is_null($this->getResource()->getTdAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->tdAttributes(
$this->getResource()->getTdAttributes()
)
)
->buttons($this->getResource()->getIndexButtons())
->clickAction($this->getResource()->getClickAction())
->when($this->getResource()->isAsync(), static function (TableBuilderContract $table): void {
$table->async()->pushState();
})
->when($this->getResource()->isStickyTable(), function (TableBuilderContract $table): void {
$table->sticky();
})
->when($this->getResource()->isColumnSelection(), function (TableBuilderContract $table): void {
$table->columnSelection();
});
}
}
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\TableBuilderContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleIndexPage extends IndexPage
{
// ...
protected function getItemsComponent(iterable $items, Fields $fields): ComponentContract
{
return TableBuilder::make(items: $items)
->name($this->getListComponentName())
->fields($fields)
->cast($this->getResource()->getCaster())
->withNotFound()
->when(
! is_null($head = $this->getResource()->getHeadRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->headRows($head)
)
->when(
! is_null($body = $this->getResource()->getRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->rows($body)
)
->when(
! is_null($foot = $this->getResource()->getFootRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->footRows($foot)
)
->when(
! is_null($this->getResource()->getTrAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->trAttributes(
$this->getResource()->getTrAttributes()
)
)
->when(
! is_null($this->getResource()->getTdAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->tdAttributes(
$this->getResource()->getTdAttributes()
)
)
->buttons($this->getResource()->getIndexButtons())
->clickAction($this->getResource()->getClickAction())
->when($this->getResource()->isAsync(), static function (TableBuilderContract $table): void {
$table->async()->pushState();
})
->when($this->getResource()->isStickyTable(), function (TableBuilderContract $table): void {
$table->sticky();
})
->when($this->getResource()->isColumnSelection(), function (TableBuilderContract $table): void {
$table->columnSelection();
});
}
}
namespaces
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\TableBuilderContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleIndexPage extends IndexPage
{
// ...
protected function getItemsComponent(iterable $items, Fields $fields): ComponentContract
{
return TableBuilder::make(items: $items)
->name($this->getListComponentName())
->fields($fields)
->cast($this->getResource()->getCaster())
->withNotFound()
->when(
! is_null($head = $this->getResource()->getHeadRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->headRows($head)
)
->when(
! is_null($body = $this->getResource()->getRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->rows($body)
)
->when(
! is_null($foot = $this->getResource()->getFootRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->footRows($foot)
)
->when(
! is_null($this->getResource()->getTrAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->trAttributes(
$this->getResource()->getTrAttributes()
)
)
->when(
! is_null($this->getResource()->getTdAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->tdAttributes(
$this->getResource()->getTdAttributes()
)
)
->buttons($this->getResource()->getIndexButtons())
->clickAction($this->getResource()->getClickAction())
->when($this->getResource()->isAsync(), static function (TableBuilderContract $table): void {
$table->async()->pushState();
})
->when($this->getResource()->isStickyTable(), function (TableBuilderContract $table): void {
$table->sticky();
})
->when($this->getResource()->isColumnSelection(), function (TableBuilderContract $table): void {
$table->columnSelection();
});
}
}
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\TableBuilderContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleIndexPage extends IndexPage
{
// ...
protected function getItemsComponent(iterable $items, Fields $fields): ComponentContract
{
return TableBuilder::make(items: $items)
->name($this->getListComponentName())
->fields($fields)
->cast($this->getResource()->getCaster())
->withNotFound()
->when(
! is_null($head = $this->getResource()->getHeadRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->headRows($head)
)
->when(
! is_null($body = $this->getResource()->getRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->rows($body)
)
->when(
! is_null($foot = $this->getResource()->getFootRows()),
fn (TableBuilderContract $table): TableBuilderContract => $table->footRows($foot)
)
->when(
! is_null($this->getResource()->getTrAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->trAttributes(
$this->getResource()->getTrAttributes()
)
)
->when(
! is_null($this->getResource()->getTdAttributes()),
fn (TableBuilderContract $table): TableBuilderContract => $table->tdAttributes(
$this->getResource()->getTdAttributes()
)
)
->buttons($this->getResource()->getIndexButtons())
->clickAction($this->getResource()->getClickAction())
->when($this->getResource()->isAsync(), static function (TableBuilderContract $table): void {
$table->async()->pushState();
})
->when($this->getResource()->isStickyTable(), function (TableBuilderContract $table): void {
$table->sticky();
})
->when($this->getResource()->isColumnSelection(), function (TableBuilderContract $table): void {
$table->columnSelection();
});
}
}
Example of an index page with the CardsBuilder
component in the Recipes section.
DetailPage
The getDetailComponent()
method allows you to change the main component of the detail page.
getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
$item
- data,
$fields
- fields.
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleDetailPage extends DetailPage
{
protected function getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
{
return TableBuilder::make($fields)
->cast($this->getResource()->getCaster())
->items([$item])
->vertical()
->simple()
->preview();
}
}
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleDetailPage extends DetailPage
{
// ...
protected function getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
{
return TableBuilder::make($fields)
->cast($this->getResource()->getCaster())
->items([$item])
->vertical()
->simple()
->preview();
}
}
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleDetailPage extends DetailPage
{
// ...
protected function getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
{
return TableBuilder::make($fields)
->cast($this->getResource()->getCaster())
->items([$item])
->vertical()
->simple()
->preview();
}
}
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleDetailPage extends DetailPage
{
// ...
protected function getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
{
return TableBuilder::make($fields)
->cast($this->getResource()->getCaster())
->items([$item])
->vertical()
->simple()
->preview();
}
}
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\UI\Components\Table\TableBuilder;
class ArticleDetailPage extends DetailPage
{
// ...
protected function getDetailComponent(?DataWrapperContract $item, Fields $fields): ComponentContract
{
return TableBuilder::make($fields)
->cast($this->getResource()->getCaster())
->items([$item])
->vertical()
->simple()
->preview();
}
}
FormPage
The getFormComponent()
method allows you to change the main component on the form page.
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Laravel\Collections\Fields;
getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract
$action
- endpoint,
$item
- data,
$fields
- fields,
$isAsync
- asynchronous mode.
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\Laravel\Pages\Crud\FormPage;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Hidden;
class ArticleFormPage extends FormPage
{
protected function getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract {
$resource = $this->getResource();
return FormBuilder::make($action)
->cast($this->getResource()->getCaster())
->fill($item)
->fields([
...$fields
->when(
! is_null($item),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_method')->setValue('PUT')
)
)
->when(
! $resource->isItemExists() && ! $resource->isCreateInModal(),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_force_redirect')->setValue(true)
)
)
->toArray(),
])
->when(
! $resource->hasErrorsAbove(),
fn (FormBuilderContract $form): FormBuilderContract => $form->errorsAbove($resource->hasErrorsAbove())
)
->when(
$isAsync,
static fn (FormBuilderContract $formBuilder): FormBuilderContract => $formBuilder
->async(events: array_filter([
$resource->getListEventName(
request()->input('_component_name', 'default'),
$isAsync && $resource->isItemExists() ? array_filter([
'page' => request()->input('page'),
'sort' => request()->input('sort'),
]) : []
),
! $resource->isItemExists() && $resource->isCreateInModal()
? AlpineJs::event(JsEvent::FORM_RESET, $resource->getUriKey())
: null,
]))
)
->when(
$resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),
static fn (FormBuilderContract $form): FormBuilderContract => $form->precognitive()
)
->when(
$resource->isSubmitShowWhen(),
static fn (FormBuilderContract $form): FormBuilderContract => $form->submitShowWhenAttribute()
)
->name($resource->getUriKey())
->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg'])
->buttons($resource->getFormBuilderButtons());
}
}
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\Laravel\Pages\Crud\FormPage;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Hidden;
class ArticleFormPage extends FormPage
{
// ...
protected function getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract {
$resource = $this->getResource();
return FormBuilder::make($action)
->cast($this->getResource()->getCaster())
->fill($item)
->fields([
...$fields
->when(
! is_null($item),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_method')->setValue('PUT')
)
)
->when(
! $resource->isItemExists() && ! $resource->isCreateInModal(),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_force_redirect')->setValue(true)
)
)
->toArray(),
])
->when(
! $resource->hasErrorsAbove(),
fn (FormBuilderContract $form): FormBuilderContract => $form->errorsAbove($resource->hasErrorsAbove())
)
->when(
$isAsync,
static fn (FormBuilderContract $formBuilder): FormBuilderContract => $formBuilder
->async(events: array_filter([
$resource->getListEventName(
request()->input('_component_name', 'default'),
$isAsync && $resource->isItemExists() ? array_filter([
'page' => request()->input('page'),
'sort' => request()->input('sort'),
]) : []
),
! $resource->isItemExists() && $resource->isCreateInModal()
? AlpineJs::event(JsEvent::FORM_RESET, $resource->getUriKey())
: null,
]))
)
->when(
$resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),
static fn (FormBuilderContract $form): FormBuilderContract => $form->precognitive()
)
->when(
$resource->isSubmitShowWhen(),
static fn (FormBuilderContract $form): FormBuilderContract => $form->submitShowWhenAttribute()
)
->name($resource->getUriKey())
->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg'])
->buttons($resource->getFormBuilderButtons());
}
}
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\Laravel\Pages\Crud\FormPage;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Hidden;
class ArticleFormPage extends FormPage
{
// ...
protected function getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract {
$resource = $this->getResource();
return FormBuilder::make($action)
->cast($this->getResource()->getCaster())
->fill($item)
->fields([
...$fields
->when(
! is_null($item),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_method')->setValue('PUT')
)
)
->when(
! $resource->isItemExists() && ! $resource->isCreateInModal(),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_force_redirect')->setValue(true)
)
)
->toArray(),
])
->when(
! $resource->hasErrorsAbove(),
fn (FormBuilderContract $form): FormBuilderContract => $form->errorsAbove($resource->hasErrorsAbove())
)
->when(
$isAsync,
static fn (FormBuilderContract $formBuilder): FormBuilderContract => $formBuilder
->async(events: array_filter([
$resource->getListEventName(
request()->input('_component_name', 'default'),
$isAsync && $resource->isItemExists() ? array_filter([
'page' => request()->input('page'),
'sort' => request()->input('sort'),
]) : []
),
! $resource->isItemExists() && $resource->isCreateInModal()
? AlpineJs::event(JsEvent::FORM_RESET, $resource->getUriKey())
: null,
]))
)
->when(
$resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),
static fn (FormBuilderContract $form): FormBuilderContract => $form->precognitive()
)
->when(
$resource->isSubmitShowWhen(),
static fn (FormBuilderContract $form): FormBuilderContract => $form->submitShowWhenAttribute()
)
->name($resource->getUriKey())
->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg'])
->buttons($resource->getFormBuilderButtons());
}
}
namespaces
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\Laravel\Pages\Crud\FormPage;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Hidden;
class ArticleFormPage extends FormPage
{
// ...
protected function getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract {
$resource = $this->getResource();
return FormBuilder::make($action)
->cast($this->getResource()->getCaster())
->fill($item)
->fields([
...$fields
->when(
! is_null($item),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_method')->setValue('PUT')
)
)
->when(
! $resource->isItemExists() && ! $resource->isCreateInModal(),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_force_redirect')->setValue(true)
)
)
->toArray(),
])
->when(
! $resource->hasErrorsAbove(),
fn (FormBuilderContract $form): FormBuilderContract => $form->errorsAbove($resource->hasErrorsAbove())
)
->when(
$isAsync,
static fn (FormBuilderContract $formBuilder): FormBuilderContract => $formBuilder
->async(events: array_filter([
$resource->getListEventName(
request()->input('_component_name', 'default'),
$isAsync && $resource->isItemExists() ? array_filter([
'page' => request()->input('page'),
'sort' => request()->input('sort'),
]) : []
),
! $resource->isItemExists() && $resource->isCreateInModal()
? AlpineJs::event(JsEvent::FORM_RESET, $resource->getUriKey())
: null,
]))
)
->when(
$resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),
static fn (FormBuilderContract $form): FormBuilderContract => $form->precognitive()
)
->when(
$resource->isSubmitShowWhen(),
static fn (FormBuilderContract $form): FormBuilderContract => $form->submitShowWhenAttribute()
)
->name($resource->getUriKey())
->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg'])
->buttons($resource->getFormBuilderButtons());
}
}
use MoonShine\Contracts\Core\TypeCasts\DataWrapperContract;
use MoonShine\Contracts\UI\ComponentContract;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Laravel\Collections\Fields;
use MoonShine\Laravel\Pages\Crud\FormPage;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Hidden;
class ArticleFormPage extends FormPage
{
// ...
protected function getFormComponent(
string $action,
?DataWrapperContract $item,
Fields $fields,
bool $isAsync = true,
): ComponentContract {
$resource = $this->getResource();
return FormBuilder::make($action)
->cast($this->getResource()->getCaster())
->fill($item)
->fields([
...$fields
->when(
! is_null($item),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_method')->setValue('PUT')
)
)
->when(
! $resource->isItemExists() && ! $resource->isCreateInModal(),
static fn (Fields $fields): Fields => $fields->push(
Hidden::make('_force_redirect')->setValue(true)
)
)
->toArray(),
])
->when(
! $resource->hasErrorsAbove(),
fn (FormBuilderContract $form): FormBuilderContract => $form->errorsAbove($resource->hasErrorsAbove())
)
->when(
$isAsync,
static fn (FormBuilderContract $formBuilder): FormBuilderContract => $formBuilder
->async(events: array_filter([
$resource->getListEventName(
request()->input('_component_name', 'default'),
$isAsync && $resource->isItemExists() ? array_filter([
'page' => request()->input('page'),
'sort' => request()->input('sort'),
]) : []
),
! $resource->isItemExists() && $resource->isCreateInModal()
? AlpineJs::event(JsEvent::FORM_RESET, $resource->getUriKey())
: null,
]))
)
->when(
$resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),
static fn (FormBuilderContract $form): FormBuilderContract => $form->precognitive()
)
->when(
$resource->isSubmitShowWhen(),
static fn (FormBuilderContract $form): FormBuilderContract => $form->submitShowWhenAttribute()
)
->name($resource->getUriKey())
->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg'])
->buttons($resource->getFormBuilderButtons());
}
}
For convenience, all CRUD pages are divided into three layers, which are responsible for displaying a certain area on the page.
TopLayer
- used for displaying metrics on the index page and for additional buttons on the edit page,
MainLayer
- this layer is used for displaying main information using FormBuilder and TableBuilder,
BottomLayer
- used for displaying additional information.
To configure the layers, the corresponding methods are used: topLayer()
, mainLayer()
, and bottomLayer()
.
The methods must return an array of Components.
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Heading;
class PostIndexPage extends IndexPage
{
protected function topLayer(): array
{
return [
Heading::make('Custom top'),
...parent::topLayer()
];
}
protected function mainLayer(): array
{
return [
Heading::make('Custom main'),
...parent::mainLayer()
];
}
protected function bottomLayer(): array
{
return [
Heading::make('Custom bottom'),
...parent::bottomLayer()
];
}
}
namespaces
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Heading;
class PostIndexPage extends IndexPage
{
// ...
protected function topLayer(): array
{
return [
Heading::make('Custom top'),
...parent::topLayer()
];
}
protected function mainLayer(): array
{
return [
Heading::make('Custom main'),
...parent::mainLayer()
];
}
protected function bottomLayer(): array
{
return [
Heading::make('Custom bottom'),
...parent::bottomLayer()
];
}
}
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Heading;
class PostIndexPage extends IndexPage
{
// ...
protected function topLayer(): array
{
return [
Heading::make('Custom top'),
...parent::topLayer()
];
}
protected function mainLayer(): array
{
return [
Heading::make('Custom main'),
...parent::mainLayer()
];
}
protected function bottomLayer(): array
{
return [
Heading::make('Custom bottom'),
...parent::bottomLayer()
];
}
}
namespaces
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Heading;
class PostIndexPage extends IndexPage
{
// ...
protected function topLayer(): array
{
return [
Heading::make('Custom top'),
...parent::topLayer()
];
}
protected function mainLayer(): array
{
return [
Heading::make('Custom main'),
...parent::mainLayer()
];
}
protected function bottomLayer(): array
{
return [
Heading::make('Custom bottom'),
...parent::bottomLayer()
];
}
}
use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\UI\Components\Heading;
class PostIndexPage extends IndexPage
{
// ...
protected function topLayer(): array
{
return [
Heading::make('Custom top'),
...parent::topLayer()
];
}
protected function mainLayer(): array
{
return [
Heading::make('Custom main'),
...parent::mainLayer()
];
}
protected function bottomLayer(): array
{
return [
Heading::make('Custom bottom'),
...parent::bottomLayer()
];
}
}
If you need to access components of a specific layer from a resource or page, use the getLayerComponents
method.
use MoonShine\Support\Enums\Layer;
$this->getFormPage()->getLayerComponents(Layer::BOTTOM);
$this->getLayerComponents(Layer::BOTTOM);
namespaces
use MoonShine\Support\Enums\Layer;
// ...
// Resource
$this->getFormPage()->getLayerComponents(Layer::BOTTOM);
// Page
$this->getLayerComponents(Layer::BOTTOM);
use MoonShine\Support\Enums\Layer;
// ...
// Resource
$this->getFormPage()->getLayerComponents(Layer::BOTTOM);
// Page
$this->getLayerComponents(Layer::BOTTOM);
namespaces
use MoonShine\Support\Enums\Layer;
// ...
// Resource
$this->getFormPage()->getLayerComponents(Layer::BOTTOM);
// Page
$this->getLayerComponents(Layer::BOTTOM);
use MoonShine\Support\Enums\Layer;
// ...
// Resource
$this->getFormPage()->getLayerComponents(Layer::BOTTOM);
// Page
$this->getLayerComponents(Layer::BOTTOM);
If you need to add a component to a specified page in the desired layer from a resource, use the resource's onLoad()
method and the page's pushToLayer()
.
use MoonShine\Permissions\Components\Permissions;
use MoonShine\Support\Enums\Layer;
protected function onLoad(): void
{
$this->getFormPage()->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
'Permissions',
$this,
)
);
}
namespaces
use MoonShine\Permissions\Components\Permissions;
use MoonShine\Support\Enums\Layer;
protected function onLoad(): void
{
$this->getFormPage()->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
'Permissions',
$this,
)
);
}
use MoonShine\Permissions\Components\Permissions;
use MoonShine\Support\Enums\Layer;
protected function onLoad(): void
{
$this->getFormPage()->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
'Permissions',
$this,
)
);
}
namespaces
use MoonShine\Permissions\Components\Permissions;
use MoonShine\Support\Enums\Layer;
protected function onLoad(): void
{
$this->getFormPage()->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
'Permissions',
$this,
)
);
}
use MoonShine\Permissions\Components\Permissions;
use MoonShine\Support\Enums\Layer;
protected function onLoad(): void
{
$this->getFormPage()->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
'Permissions',
$this,
)
);
}
We do not recommend using CRUD pages to arbitrary URL.
However, if you understand their logic well, you can use CRUD pages on non-standard routes, emulating the necessary URL.
class HomeController extends Controller
{
public function __invoke(FormArticlePage $page, ArticleResource $resource)
{
return $page->simulateRoute($page, $resource)->loaded();
}
}
class HomeController extends Controller
{
public function __invoke(FormArticlePage $page, ArticleResource $resource)
{
return $page->simulateRoute($page, $resource)->loaded();
}
}
class HomeController extends Controller
{
public function __invoke(FormArticlePage $page, ArticleResource $resource)
{
return $page->simulateRoute($page, $resource)->loaded();
}
}
class HomeController extends Controller
{
public function __invoke(FormArticlePage $page, ArticleResource $resource)
{
return $page->simulateRoute($page, $resource)->loaded();
}
}
class HomeController extends Controller
{
public function __invoke(FormArticlePage $page, ArticleResource $resource)
{
return $page->simulateRoute($page, $resource)->loaded();
}
}