Основы
MoonShine предоставляет возможность настройки страниц crud ModelResource, для этого необходимо при создании ресурса через команду выбрать тип ресурса Model resource with pages
.
Это создаст класс ресурса модели и дополнительные классы для страниц индекса, детального просмотра и формы. Классы страниц по умолчанию будут располагаться в директории app/MoonShine/Pages
.
В созданном ресурсе модели страницы crud будут зарегистрированы в методе pages()
.
namespace App\MoonShine\Resources;use App\Models\Post;use App\MoonShine\Pages\Post\PostIndexPage;use App\MoonShine\Pages\Post\PostFormPage;use App\MoonShine\Pages\Post\PostDetailPage;use MoonShine\Resources\ModelResource;class PostResource extends ModelResource{protected string $model = Post::class;protected string $title = 'Posts';//...public function pages(): array{return [PostIndexPage::make($this->title()),PostFormPage::make($this->getItemID()? __('moonshine::ui.edit'): __('moonshine::ui.add')),PostDetailPage::make(__('moonshine::ui.show')),];}//...}
namespace App\MoonShine\Resources;use App\Models\Post;use App\MoonShine\Pages\Post\PostIndexPage;use App\MoonShine\Pages\Post\PostFormPage;use App\MoonShine\Pages\Post\PostDetailPage;use MoonShine\Resources\ModelResource;class PostResource extends ModelResource{protected string $model = Post::class;protected string $title = 'Posts';//...public function pages(): array{return [PostIndexPage::make($this->title()),PostFormPage::make($this->getItemID()? __('moonshine::ui.edit'): __('moonshine::ui.add')),PostDetailPage::make(__('moonshine::ui.show')),];}//...}
PageType
Для указания типа страницы в ModelResource используется enum класс PageType
.
Доступны следующие типы страниц:
INDEX
- страница индекса,FORM
- страница формы,DETAIL
- страница детального просмотра.
use MoonShine\Enums\PageType;//...PageType::INDEX;PageType::FORM;PageType::DETAIL;
use MoonShine\Enums\PageType;//...PageType::INDEX;PageType::FORM;PageType::DETAIL;
Добавление полей
Поля в MoonShine используются не только для ввода данных, но и для их вывода.
Метод fields()
в классе страницы crud позволяет указать необходимые поля.
namespace App\MoonShine\Pages\Post;use MoonShine\Pages\Crud\IndexPage;class PostIndexPage extends IndexPage{public function fields(): array{return [ID::make(),Text::make('Title'),];}//...}
namespace App\MoonShine\Pages\Post;use MoonShine\Pages\Crud\IndexPage;class PostIndexPage extends IndexPage{public function fields(): array{return [ID::make(),Text::make('Title'),];}//...}
Основные компоненты
В админ-панели MoonShine можно быстро изменить основной компонент на странице.
IndexPage
Метод itemsComponent()
позволяет изменить основной компонент страницы индекса.
itemsComponent(iterable $items, Fields $fields)
itemsComponent(iterable $items, Fields $fields)
$items
- значения полей,$fields
- поля.
use MoonShine\Components\TableBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Fields\Fields;use MoonShine\Pages\Crud\IndexPage;class ArticleIndexPage extends IndexPage{// ...protected function itemsComponent(iterable $items, Fields $fields): MoonShineRenderable{return TableBuilder::make(items: $items)->name($this->listComponentName())->fields($fields)->cast($this->getResource()->getModelCast())->withNotFound()->when(! is_null($this->getResource()->trAttributes()),fn (TableBuilder $table): TableBuilder => $table->trAttributes($this->getResource()->trAttributes()))->when(! is_null($this->getResource()->tdAttributes()),fn (TableBuilder $table): TableBuilder => $table->tdAttributes($this->getResource()->tdAttributes()))->buttons($this->getResource()->getIndexItemButtons())->customAttributes(['data-click-action' => $this->getResource()->getClickAction(),])->when($this->getResource()->isAsync(), function (TableBuilder $table): void {$table->async()->customAttributes(['data-pushstate' => 'true',]);});}}
use MoonShine\Components\TableBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Fields\Fields;use MoonShine\Pages\Crud\IndexPage;class ArticleIndexPage extends IndexPage{// ...protected function itemsComponent(iterable $items, Fields $fields): MoonShineRenderable{return TableBuilder::make(items: $items)->name($this->listComponentName())->fields($fields)->cast($this->getResource()->getModelCast())->withNotFound()->when(! is_null($this->getResource()->trAttributes()),fn (TableBuilder $table): TableBuilder => $table->trAttributes($this->getResource()->trAttributes()))->when(! is_null($this->getResource()->tdAttributes()),fn (TableBuilder $table): TableBuilder => $table->tdAttributes($this->getResource()->tdAttributes()))->buttons($this->getResource()->getIndexItemButtons())->customAttributes(['data-click-action' => $this->getResource()->getClickAction(),])->when($this->getResource()->isAsync(), function (TableBuilder $table): void {$table->async()->customAttributes(['data-pushstate' => 'true',]);});}}
Пример страницы индекса с компонентом CardsBuilder в разделе Рецепты
DetailPage
Метод detailComponent()
позволяет изменить основной компонент страницы детального просмотра.
detailComponent(?Model $item, Fields $fields)
detailComponent(?Model $item, Fields $fields)
-$item
- Eloquent Model
-$fields
- поля
use Illuminate\Database\Eloquent\Model;use Illuminate\View\ComponentAttributeBag;use MoonShine\Components\TableBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Fields\Fields;use MoonShine\Pages\Crud\DetailPage;class ArticleDetailPage extends DetailPage{// ...protected function detailComponent(?Model $item, Fields $fields): MoonShineRenderable{return TableBuilder::make($fields)->cast($this->getResource()->getModelCast())->items([$item])->vertical()->simple()->preview()->tdAttributes(fn ($data,int $row,int $cell,ComponentAttributeBag $attributes): ComponentAttributeBag => $attributes->when($cell === 0,fn (ComponentAttributeBag $attr): ComponentAttributeBag => $attr->merge(['class' => 'font-semibold','width' => '20%',])));}}
use Illuminate\Database\Eloquent\Model;use Illuminate\View\ComponentAttributeBag;use MoonShine\Components\TableBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Fields\Fields;use MoonShine\Pages\Crud\DetailPage;class ArticleDetailPage extends DetailPage{// ...protected function detailComponent(?Model $item, Fields $fields): MoonShineRenderable{return TableBuilder::make($fields)->cast($this->getResource()->getModelCast())->items([$item])->vertical()->simple()->preview()->tdAttributes(fn ($data,int $row,int $cell,ComponentAttributeBag $attributes): ComponentAttributeBag => $attributes->when($cell === 0,fn (ComponentAttributeBag $attr): ComponentAttributeBag => $attr->merge(['class' => 'font-semibold','width' => '20%',])));}}
FormPage
Метод formComponent()
позволяет изменить основной компонент на странице с формой.
formComponent(string $action,?Model $item,Fields $fields,bool $isAsync = false,): MoonShineRenderable
formComponent(string $action,?Model $item,Fields $fields,bool $isAsync = false,): MoonShineRenderable
-$action
- действие,
-$item
- Eloquent Model,
-$fields
- поля,
-$isAsync
- асинхронный режим.
use Illuminate\Database\Eloquent\Model;use Illuminate\View\ComponentAttributeBag;use MoonShine\Components\FormBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Enums\JsEvent;use MoonShine\Fields\Fields;use MoonShine\Fields\Hidden;use MoonShine\Pages\Crud\FormPage;use MoonShine\Support\AlpineJs;class ArticleFormPage extends FormPage{// ...protected function formComponent(string $action,?Model $item,Fields $fields,bool $isAsync = false,): MoonShineRenderable {$resource = $this->getResource();return FormBuilder::make($action)->fillCast($item,$resource->getModelCast())->fields($fields->when(! is_null($item),fn (Fields $fields): Fields => $fields->push(Hidden::make('_method')->setValue('PUT')))->when(! $item?->exists && ! $resource->isCreateInModal(),fn (Fields $fields): Fields => $fields->push(Hidden::make('_force_redirect')->setValue(true)))->toArray())->when($isAsync,fn (FormBuilder $formBuilder): FormBuilder => $formBuilder->async(asyncEvents: [$resource->listEventName(request('_component_name', 'default')),AlpineJs::event(JsEvent::FORM_RESET, 'crud'),]))->when($resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),fn (FormBuilder $form): FormBuilder => $form->precognitive())->name('crud')->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg']);}}
use Illuminate\Database\Eloquent\Model;use Illuminate\View\ComponentAttributeBag;use MoonShine\Components\FormBuilder;use MoonShine\Contracts\MoonShineRenderable;use MoonShine\Enums\JsEvent;use MoonShine\Fields\Fields;use MoonShine\Fields\Hidden;use MoonShine\Pages\Crud\FormPage;use MoonShine\Support\AlpineJs;class ArticleFormPage extends FormPage{// ...protected function formComponent(string $action,?Model $item,Fields $fields,bool $isAsync = false,): MoonShineRenderable {$resource = $this->getResource();return FormBuilder::make($action)->fillCast($item,$resource->getModelCast())->fields($fields->when(! is_null($item),fn (Fields $fields): Fields => $fields->push(Hidden::make('_method')->setValue('PUT')))->when(! $item?->exists && ! $resource->isCreateInModal(),fn (Fields $fields): Fields => $fields->push(Hidden::make('_force_redirect')->setValue(true)))->toArray())->when($isAsync,fn (FormBuilder $formBuilder): FormBuilder => $formBuilder->async(asyncEvents: [$resource->listEventName(request('_component_name', 'default')),AlpineJs::event(JsEvent::FORM_RESET, 'crud'),]))->when($resource->isPrecognitive() || (moonshineRequest()->isFragmentLoad('crud-form') && ! $isAsync),fn (FormBuilder $form): FormBuilder => $form->precognitive())->name('crud')->submit(__('moonshine::ui.save'), ['class' => 'btn-primary btn-lg']);}}
Слои на странице
Для удобства все страницы crud разделены на три слоя, которые отвечают за отображение определенной области на странице.
TopLayer
- используется для отображения метрик на странице индекса и для дополнительных кнопок на странице редактированияMainLayer
- этот слой используется для отображения основной информации с помощью FormBuilder и TableBuilderBottomLayer
- используется для отображения дополнительной информации
Для настройки слоев используются соответствующие методы: topLayer()
, mainLayer()
и bottomLayer()
. Методы должны возвращать массив Компонентов.
namespace App\MoonShine\Pages\Post;use MoonShine\Decorations\Heading;use MoonShine\Pages\Crud\IndexPage;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()];}//...}
namespace App\MoonShine\Pages\Post;use MoonShine\Decorations\Heading;use MoonShine\Pages\Crud\IndexPage;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()];}//...}
Если вам нужно получить доступ к компонентам определенного слоя через ресурс или страницу, то используйте метод getLayerComponents
.
use MoonShine\Enums\Layer;use MoonShine\Enums\PageType;// ...// Resource$this->getPages()->findByType(PageType::FORM)->getLayerComponents(Layer::BOTTOM);// Page$this->getLayerComponents(Layer::BOTTOM);
use MoonShine\Enums\Layer;use MoonShine\Enums\PageType;// ...// Resource$this->getPages()->findByType(PageType::FORM)->getLayerComponents(Layer::BOTTOM);// Page$this->getLayerComponents(Layer::BOTTOM);
Если вам нужно добавить компонент для указанной страницы в нужный слой через ресурс, то используйте метод onBoot
ресурса и pushToLayer
страницы.
protected function onBoot(): void{$this->getPages()->findByUri(PageType::FORM->value)->pushToLayer(layer: Layer::BOTTOM,component: Permissions::make('Permissions',$this,));}
protected function onBoot(): void{$this->getPages()->findByUri(PageType::FORM->value)->pushToLayer(layer: Layer::BOTTOM,component: Permissions::make('Permissions',$this,));}