Recipes

Форма и события

Форма при успешном запросе обновляет таблицу и сбрасывает значения.

Block::make([
FormBuilder::make(route('form-table.store'))
->fields([
Text::make('Title')
])
->name('main-form')
->async(asyncEvents: ['table-updated-main-table','form-reset-main-form'])
]),
 
TableBuilder::make()
->fields([
ID::make(),
Text::make('Title'),
Textarea::make('Body'),
])
->creatable()
->items(Post::query()->paginate())
->name('main-table')
->async()

Давайте также рассмотрим как добавить свои события

<div x-data=""
@my-event.window="alert()"
>
</div>
<div x-data="my"
@my-event.window="asyncRequest"
>
</div>
 
<script>
document.addEventListener("alpine:init", () => {
Alpine.data("my", () => ({
init() {
 
},
asyncRequest() {
this.$event.preventDefault()
 
// this.$el
// this.$root
}
}))
})
</script>
FormBuilder::make(route('form-table.store'))
->fields([
Text::make('Title')
])
->name('main-form')
->async(asyncEvents: ['my-event'])

View компонента с AlpineJs

Также рекомендуем ознакомиться с AlpineJs и использовать всю мощь этого js фреймворка.

Вы можете использовать его реактивность, давайте посмотрим как удобно создать компонент.

<div x-data="myComponent">
</div>
 
<script>
document.addEventListener("alpine:init", () => {
Alpine.data("myComponent", () => ({
init() {
 
},
}))
})
</script>

Подключение Vite build

Добавим скомпилированный с помощью Vite build.

use Illuminate\Support\Facades\Vite;
 
class MoonShineServiceProvider extends MoonShineApplicationServiceProvider
{
//...
 
public function boot(): void
{
parent::boot();
 
moonShineAssets()->add([
Vite::asset('resources/css/app.css'),
Vite::asset('resources/js/app.js'),
]);
}
 
//...
}

Кастомные кнопки

Добавим кастомные кнопки в индексную таблицу.

public function indexButtons(): array
{
$resource = new CommentResource();
return [
ActionButton::make('Custom button', static fn ($data): string => to_page(
page: $resource->formPage(),
resource: $resource,
params: ['resourceItem' => $data->getKey()]
))
];
}

HasOne через поле Template

Пример реализации отношения HasOne через поле Template.

use MoonShine\Fields\Template;
 
//...
 
public function fields(): array
{
return [
Template::make('Comment')
->changeFill(fn (Article $data) => $data->comment)
->changePreview(fn($data) => $data?->id ?? '-')
->fields((new CommentResource())->getFormFields())
->changeRender(function (?Comment $data, Template $field) {
$fields = $field->preparedFields();
$fields->fill($data?->toArray() ?? [], $data ?? new Comment());
 
return Components::make($fields);
})
->onAfterApply(function (Article $item, array $value) {
$item->comment()->updateOrCreate([
'id' => $value['id']
], $value);
 
return $item;
})
];
}
 
//...

Изменение breadcrumbs из ресурса

Изменять хлебные крошки страниц можно прямо из ресурса.

namespace App\MoonShine\Resources;
 
use App\Models\Post;
use MoonShine\Resources\ModelResource;
 
class PostResource extends ModelResource
{
//...
 
protected function onBoot(): void
{
$this->formPage()
->setBreadcrumbs([
'#' => $this->title()
]);
}
 
//...
}

Индексная страница через CardsBuilder

Изменим на индексной странице отображение элементов через компонент CardsBuilder.

class MoonShineUserIndexPage extends IndexPage
{
public function listComponentName(): string
{
return 'index-cards';
}
 
public function listEventName(): string
{
return 'cards-updated';
}
 
protected function itemsComponent(iterable $items, Fields $fields): MoonShineRenderable
{
return CardsBuilder::make($items, $fields)
->cast($this->getResource()->getModelCast())
->name($this->listComponentName())
->async()
->overlay()
->title('email')
->subtitle('name')
->url(fn ($user) => $this->getResource()->formPageUrl($user))
->thumbnail(fn ($user) => asset($user->avatar))
->buttons($this->getResource()->getIndexItemButtons());
}
}

Сортировка для CardsBuilder

Создадим сортировку для компонента CardsBuilder:

Select::make('Sorts')->options([
'created_at' => 'Date',
'id' => 'ID',
])
->onChangeMethod('reSort', events: ['cards-updated-cards'])
->setValue(session('sort_column') ?: 'created_at'),
 
 
CardsBuilder::make(
items: Article::query()->with('author')
->when(
session('sort_column'),
fn($q) => $q->orderBy(session('sort_column'), session('sort_direction', 'asc')),
fn($q) => $q->latest()
)
->paginate()
)
->name('cards')
->async()
->cast(ModelCast::make(Article::class))
->title('title')
->url(fn($data) => (new ArticleResource())->formPageUrl($data))
->overlay()
->columnSpan(4) ,
 
// ...
 
public function reSort(MoonShineRequest $request): void
{
session()->put('sort_column', $request->get('value'));
session()->put('sort_direction', 'ASC');
}

updateOnPreview для pivot полей

Реализация через asyncMethod метод изменения pivot поля на индексной странице:

public function fields(): array
{
return [
Grid::make([
Column::make([
ID::make()->sortable(),
Text::make('Team title')->required(),
Number::make('Team number'),
BelongsTo::make('Tournament', resource: new TournamentResource())
->searchable(),
]),
Column::make([
BelongsToMany::make('Users', resource: new UserResource())
->fields([
Switcher::make('Approved')
->updateOnPreview(MoonShineRouter::asyncMethodClosure(
'updatePivot',
params: fn($data) => ['parent' => $data->pivot->tournamen_team_id]
)),
])
->searchable(),
])
])
];
}
 
public function updatePivot(MoonShineRequest $request): MoonShineJsonResponse
{
$item = TournamentTeam::query()
->findOrFail($request->get('parent'));
 
$column = (string) $request->str('field')->remove('pivot.');
 
$item-> users()->updateExistingPivot($request->get('resourceItem'), [
$column => $request->get('value'),
]);
 
return MoonShineJsonResponse::make()
->toast('Success');
}

ID родителя в HasMany

Связь HasMany хранит данные файлов, которые необходимо сохранять в директории по id родителя.

use App\Models\PostImage;
use MoonShine\Fields\ID;
use MoonShine\Fields\Image;
use MoonShine\Fields\Relationships\BelongsTo;
use MoonShine\Resources\ModelResource;
use MoonShine\Traits\Resource\ResourceWithParent;
 
class PostImageResource extends ModelResource
{
use ResourceWithParent;
 
public string $model = PostImage::class;
 
protected function getParentResourceClassName(): string
{
return PostResource::class;
}
 
protected function getParentRelationName(): string
{
return 'post';
}
 
public function fields(): array
{
return [
ID::make(),
Image::make('Path')
->when(
$parentId = $this->getParentId(),
fn(Image $image) => $image->dir('post_images/'.$parentId)
)
,
BelongsTo::make('Post', 'post', resource: new PostResource())
];
}
 
//...
}