# Основы
MoonShine предоставляет возможность кастомизировать crud страниц ModelResource,
для этого необходимо, при создании ресурса через команду,
выбрать тип ресурса
Model resource with pages
.
В результате будет создан класс ресурса модели и дополнительные классы для индексной, детальной и страницы с формой.
Располагаться классы страниц по умолчанию будут в директории app/MoonShine/Pages
.
В созданном ресурсе модели в методе pages()
будут зарегистрированы crud страницы.
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 class PageType
Доступны следующие типы страниц:
INDEX
- индексная страница,FORM
- страница с формой,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'), ]; } //...}
# Основные компоненты
В админ-панели MoonShine можно быстро изменить основной компонент на странице.
Метод itemsComponent()
позволяет изменить основной компонент индексной страницы.
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', ]); }); } }
Пример индексной страницы с компонентом CardsBuilder в разделе Recipes
Метод detailComponent()
позволяет изменить основной компонент детальной страницы.
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%', ]) )); } }
Метод formComponent()
позволяет изменить основной компонент на странице с формой.
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']); } }
# Слои на странице
Для удобства все crud страницы разбиты на три слоя, которые отвечают за отображение определенной области на странице.
-
TopLayer
- по умолчанию используется для вывода метрик на индексной странице и для дополнительных кнопок на странице редактирования -
MainLayer
- по умолчанию данный слой используется для вывода основной информации используя FormBuilder и TableBuilder BottomLayer
- по умолчанию используется для вывода дополнительной информации
Для кастомизации слоев используются соответствующие методы: 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() ]; } //...}
![](https://moonshine-laravel.com/screenshots/page_index_layers.png)
![](https://moonshine-laravel.com/screenshots/page_form_layers.png)
![](https://moonshine-laravel.com/screenshots/page_detail_layers.png)
Если необходимо через ресурс или страницу получить доступ к компонентам определенного слоя, то
воспользуйтесь методом getLayerComponents
use MoonShine\Enums\Layer;use MoonShine\Enums\PageType; // ... // Resource$this->getPages() ->formPage() ->getLayerComponents(Layer::BOTTOM); // Page$this->getLayerComponents(Layer::BOTTOM);
Если необходимо через ресурс добавить компонент для указанной страницы в нужный слой, то воспользуйтесь методом onBoot ресурса и pushToLayer страницы
protected function onBoot(): void{ $this->getPages() ->formPage() ->pushToLayer( layer: Layer::BOTTOM, component: Permissions::make( 'Permissions', $this, ) );}