- Основы
- Создание
- Базовые свойства
- Объявление в системе
- Автозагрузка
- Сортировка
- Пагинация
- Асинхронный режим
- Добавление в меню
- Alias
- Текущий элемент/модель
- Модальные окна
- Редиректы
- Активные действия
- Жизненный цикл
- Assets
- Response модификаторы
- Обработчики CRUD-операций
Основы
ModelResource расширяет CrudResource и предоставляет функциональность для работы с моделями Eloquent.
Он обеспечивает основу для создания ресурсов, связанных с моделями базы данных.
ModelResource предоставляет методы для выполнения CRUD-операций, управления отношениями, применения фильтров и многое другое.
Вы также можете ознакомиться с разделом CrudResource.
CrudResource - это абстрактный класс предоставляющий базовый интерфейс для CRUD операций без привязки к хранилищу и типу данных.
Под капотом, ModelResource расширяет CrudResource и сразу включает возможность работы с Eloquent.
Если углубляться в детали MoonShine, то вы увидите все те же стандартные Controller, Model и Blade views.
Если бы вы разрабатывали самостоятельно, то создать ресурс контроллеры и ресурс маршруты можно следующим образом:
php artisan make:controller Controller --resourcephp artisan make:controller Controller --resource
use Illuminate\Support\Facades\Route;Route::resource('resources', Controller::class);
use Illuminate\Support\Facades\Route;Route::resource('resources', Controller::class);
Но эту работу можно поручить админ-панели MoonShine, которая будет их генерировать и объявлять самостоятельно.
ModelResource является основным компонентом для создания раздела в админ-панели при работе с базой данных.
Создание
php artisan moonshine:resource Postphp artisan moonshine:resource Post
Для более подробной информации обратитесь к разделу Команды.
Базовые свойства
Базовые параметры, которые можно менять у ресурса, чтобы кастомизировать его работу.
namespace App\MoonShine\Resources;use App\Models\Post;use MoonShine\Laravel\Resources\ModelResource;/*** @extends ModelResource<Post>*/class PostResource extends ModelResource{// Модельprotected string $model = Post::class;// Заголовок разделаprotected string $title = 'Posts';// Eager loadprotected array $with = ['category'];// Поле для отображения значений в связях и хлебных крошкахprotected string $column = 'id';// ...}
namespace App\MoonShine\Resources;use App\Models\Post;use MoonShine\Laravel\Resources\ModelResource;/*** @extends ModelResource<Post>*/class PostResource extends ModelResource{// Модельprotected string $model = Post::class;// Заголовок разделаprotected string $title = 'Posts';// Eager loadprotected array $with = ['category'];// Поле для отображения значений в связях и хлебных крошкахprotected string $column = 'id';// ...}

Объявление в системе
Ресурс автоматически регистрируется в MoonShineServiceProvider при вызове команды php artisan moonshine:resource.
Но если вы создаете раздел вручную, то вам необходимо самостоятельно его объявить в системе в MoonShineServiceProvider.
namespace App\Providers;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Contracts\Core\DependencyInjection\ConfiguratorContract;use MoonShine\Laravel\DependencyInjection\MoonShine;use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator;class MoonShineServiceProvider extends ServiceProvider{/*** @param MoonShine $core* @param MoonShineConfigurator $config**/public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->resources([MoonShineUserResource::class,MoonShineUserRoleResource::class,ArticleResource::class,// ...])->pages([...$config->getPages(),]);}}
namespace App\Providers;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Contracts\Core\DependencyInjection\ConfiguratorContract;use MoonShine\Laravel\DependencyInjection\MoonShine;use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator;class MoonShineServiceProvider extends ServiceProvider{/*** @param MoonShine $core* @param MoonShineConfigurator $config**/public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->resources([MoonShineUserResource::class,MoonShineUserRoleResource::class,ArticleResource::class,// ...])->pages([...$config->getPages(),]);}}
Автозагрузка
В MoonShine также доступна автозагрузка страниц и ресурсов.
Она выключена по-умолчанию и для активации нужно вызвать метод autoload() в MoonShineServiceProvider вместо указания ссылок на страницы и ресурсы.
namespace App\Providers;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Contracts\Core\DependencyInjection\ConfiguratorContract;class MoonShineServiceProvider extends ServiceProvider{public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->autoload();}}
namespace App\Providers;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Contracts\Core\DependencyInjection\ConfiguratorContract;class MoonShineServiceProvider extends ServiceProvider{public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->autoload();}}
При деплое проекта на продакшен рекомендуется вызывать консольную команду php artisan optimize.
Помимо её основных функций, она также выполнит кэширование ресурсов MoonShine.
Очистить кэш панели можно как командой php artisan optimize:clear,
так и прямым вызовом консольной команды php artisan moonshine:optimize-clear.
Если после создания классов приложение их не видит - обновите кэш композера командой composer dump-autoload.
Сортировка
По умолчанию записи в таблице ресурса сортируются по полю id в порядке убывания.
Вы можете изменить сортировку с помощью свойств $sortColumn и $sortDirection.
protected string $sortColumn = 'created_at';protected SortDirection $sortDirection = SortDirection::DESC;protected string $sortColumn = 'created_at';protected SortDirection $sortDirection = SortDirection::DESC;
Пагинация
По умолчанию MoonShine использует стандартную пагинацию Laravel.
Вы можете переключиться на cursor pagination или simple pagination с помощью свойств $cursorPaginate и $simplePaginate.
protected bool $cursorPaginate = true;protected bool $cursorPaginate = true;
protected bool $simplePaginate = true;protected bool $simplePaginate = true;
Подробнее о типах пагинации можно узнать в документации Laravel.
Асинхронный режим
По умолчанию в ресурсе включен "Асинхронный режим".
Чтобы его выключить, переопределите свойство $isAsync в ресурсе или на отдельных CRUD страницах.
protected bool $isAsync = false;protected bool $isAsync = false;
Подробнее об асинхронной загрузке таблицы можно узнать в разделе TableBuilder.
Подробнее об асинхронной отправке форм можно узнать в разделе FormBuilder.
Добавление в меню
Все страницы в MoonShine имеют Layout и у каждой страницы он может быть свой.
По умолчанию при установке MoonShine добавляет базовый MoonShineLayout в директорию app/MoonShine/Layouts.
В Layout кастомизируется всё, что отвечает за внешний вид ваших страниц и это касается также и навигации.
Чтобы добавить раздел в меню, необходимо объявить его через метод menu() в Layout.
namespace App\MoonShine\Layouts;use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\MenuManager\MenuGroup;use MoonShine\MenuManager\MenuItem;final class MoonShineLayout extends AppLayout{// ...protected function menu(): array{return [MenuGroup::make(__('moonshine::ui.resource.system'), [MenuItem::make(MoonShineUserResource::class),MenuItem::make(MoonShineUserRoleResource::class),]),MenuItem::make(PostResource::class),// ...];}}
namespace App\MoonShine\Layouts;use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\MenuManager\MenuGroup;use MoonShine\MenuManager\MenuItem;final class MoonShineLayout extends AppLayout{// ...protected function menu(): array{return [MenuGroup::make(__('moonshine::ui.resource.system'), [MenuItem::make(MoonShineUserResource::class),MenuItem::make(MoonShineUserRoleResource::class),]),MenuItem::make(PostResource::class),// ...];}}
О расширенных настройках Layout можно узнать в разделе Layout.
О расширенных настройках MenuManager можно узнать в разделе Menu.
Alias
По умолчанию alias ресурса, который используется в url, генерируется на основе наименования класс в kebab-case, например:
MoonShineUserResource -> moon-shine-user-resource.
Для того чтобы изменить alias, можно воспользоваться свойством ресурса $alias или методом getAlias().
class PostResource extends ModelResource{protected ?string $alias = 'custom-alias';// ...}class PostResource extends ModelResource{protected ?string $alias = 'custom-alias';// ...}
namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{public function getAlias(): ?string{return 'custom-alias';}}
namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{public function getAlias(): ?string{return 'custom-alias';}}
Текущий элемент/модель
Если в url детальной страницы или страницы редактирования присутствует параметр resourceItem,
то в ресурсе вы можете получить доступ к текущему элементу через метод getItem().
$this->getItem();$this->getItem();
Через метод getModel() можно получить доступ к модели.
$this->getModel();$this->getModel();
Модальные окна
Вы можете добавлять, редактировать и просматривать записи прямо на странице со списком в модальном окне.
class PostResource extends ModelResource{protected bool $createInModal = true;protected bool $editInModal = true;protected bool $detailInModal = true;// ...}class PostResource extends ModelResource{protected bool $createInModal = true;protected bool $editInModal = true;protected bool $detailInModal = true;// ...}
Модификаторы модальных окон
Вы можете настраивать внешний вид и поведение модальных окон в ресурсе, переопределив методы-модификаторы.
Create Modal
Метод modifyCreateModal() позволяет изменить модальное окно создания записи.
use MoonShine\Contracts\UI\ModalContract;protected function modifyCreateModal(ModalContract $modal): ModalContract{return $modal->full();}
use MoonShine\Contracts\UI\ModalContract;protected function modifyCreateModal(ModalContract $modal): ModalContract{return $modal->full();}
Edit Modal
Метод modifyEditModal() позволяет изменить модальное окно редактирования записи.
use MoonShine\Contracts\UI\ModalContract;protected function modifyEditModal(ModalContract $modal): ModalContract{return $modal->full();}
use MoonShine\Contracts\UI\ModalContract;protected function modifyEditModal(ModalContract $modal): ModalContract{return $modal->full();}
Detail Modal
Метод modifyDetailModal() позволяет изменить модальное окно детального просмотра.
use MoonShine\Contracts\UI\ModalContract;protected function modifyDetailModal(ModalContract $modal): ModalContract{return $modal->full();}
use MoonShine\Contracts\UI\ModalContract;protected function modifyDetailModal(ModalContract $modal): ModalContract{return $modal->full();}
Delete Modal
Метод modifyDeleteModal() позволяет изменить модальное окно удаления записи.
use MoonShine\Contracts\UI\ModalContract;protected function modifyDeleteModal(ModalContract $modal): ModalContract{return $modal->auto();}
use MoonShine\Contracts\UI\ModalContract;protected function modifyDeleteModal(ModalContract $modal): ModalContract{return $modal->auto();}
Mass Delete Modal
Метод modifyMassDeleteModal() позволяет изменить модальное окно массового удаления.
use MoonShine\Contracts\UI\ModalContract;protected function modifyMassDeleteModal(ModalContract $modal): ModalContract{return $modal->auto();}
use MoonShine\Contracts\UI\ModalContract;protected function modifyMassDeleteModal(ModalContract $modal): ModalContract{return $modal->auto();}
Filters OffCanvas
Метод modifyFiltersOffCanvas() позволяет изменить боковую панель фильтров.
use MoonShine\Contracts\UI\OffCanvasContract;protected function modifyFiltersOffCanvas(OffCanvasContract $offCanvas): OffCanvasContract{return $offCanvas->full()->autoClose(false);}
use MoonShine\Contracts\UI\OffCanvasContract;protected function modifyFiltersOffCanvas(OffCanvasContract $offCanvas): OffCanvasContract{return $offCanvas->full()->autoClose(false);}
Все методы модификаторов применяются к соответствующим модальным окнам и боковым панелям, позволяя гибко настроить их внешний вид и поведение.
Редиректы
По умолчанию при создании и редактировании записи осуществляется редирект на страницу с формой, но это поведение можно контролировать.
Через свойство в ресурсе:
use MoonShine\Support\Enums\PageType;protected ?PageType $redirectAfterSave = PageType::FORM;
use MoonShine\Support\Enums\PageType;protected ?PageType $redirectAfterSave = PageType::FORM;
Через метод:
public function getRedirectAfterSave(): string{return '/';}public function getRedirectAfterSave(): string{return '/';}
Также доступен редирект после удаления:
public function getRedirectAfterDelete(): string{return $this->getIndexPageUrl();}public function getRedirectAfterDelete(): string{return $this->getIndexPageUrl();}
Активные действия
Часто бывает, что необходимо создать ресурс, в котором будет исключена возможность удалять, или добавлять, или редактировать.
И здесь речь не об авторизации, а о глобальном исключении этих разделов.
Делается это крайне просто за счет метода activeActions() в ресурсе.
use MoonShine\Support\Enums\Action;use MoonShine\Support\ListOf;class PostResource extends ModelResource{// ...protected function activeActions(): ListOf{return parent::activeActions()->except(Action::VIEW, Action::MASS_DELETE)// ->only(Action::VIEW);}}
use MoonShine\Support\Enums\Action;use MoonShine\Support\ListOf;class PostResource extends ModelResource{// ...protected function activeActions(): ListOf{return parent::activeActions()->except(Action::VIEW, Action::MASS_DELETE)// ->only(Action::VIEW);}}
Также можно просто создать новый список, например:
use MoonShine\Support\Enums\Action;use MoonShine\Support\ListOf;protected function activeActions(): ListOf{return new ListOf(Action::class, [Action::VIEW, Action::UPDATE]);}
use MoonShine\Support\Enums\Action;use MoonShine\Support\ListOf;protected function activeActions(): ListOf{return new ListOf(Action::class, [Action::VIEW, Action::UPDATE]);}
Жизненный цикл
Resource имеет несколько различных методов подключения к различным частям своего жизненного цикла. Давайте пройдемся по ним:
Активный ресурс
Метод onLoad() дает возможность интегрироваться в момент когда ресурс загружен и в данный момент является активным.
class PostResource extends ModelResource{// ...protected function onLoad(): void{// ...}}class PostResource extends ModelResource{// ...protected function onLoad(): void{// ...}}
Рецепт: Изменение breadcrumbs из ресурса.
Вы также можете подключить trait к ресурсу и внутри trait добавить метод согласно конвенции наименований - load{TraitName}
и через трейт обратиться к onLoad() ресурса.
use App\Traits\WithPermissions;class PostResource extends ModelResource{use WithPermissions;// ...}
use App\Traits\WithPermissions;class PostResource extends ModelResource{use WithPermissions;// ...}
use MoonShine\Support\Enums\Layer;use MoonShine\Support\Enums\PageType;trait WithPermissions{protected function loadWithPermissions(): void{$this->getPages()->findByUri(PageType::FORM->value)->pushToLayer(layer: Layer::BOTTOM,component: Permissions::make(label: 'Permissions',resource: $this,));}}
use MoonShine\Support\Enums\Layer;use MoonShine\Support\Enums\PageType;trait WithPermissions{protected function loadWithPermissions(): void{$this->getPages()->findByUri(PageType::FORM->value)->pushToLayer(layer: Layer::BOTTOM,component: Permissions::make(label: 'Permissions',resource: $this,));}}
Создание экземпляра
Метод onBoot() дает возможность интегрироваться в момент когда MoonShine создает экземпляр ресурса в системе.
class PostResource extends ModelResource{// ...protected function onBoot(): void{// ...}}class PostResource extends ModelResource{// ...protected function onBoot(): void{// ...}}
Вы также можете подключить trait к ресурсу и внутри trait добавить метод согласно конвенции наименований - boot{TraitName}
и через трейт обратиться к onBoot() ресурса.
Assets
use MoonShine\AssetManager\Css;use MoonShine\AssetManager\Js;protected function onLoad(): void{$this->getAssetManager()->add(Css::make('/css/app.css'))->append(Js::make('/js/app.js'));}
use MoonShine\AssetManager\Css;use MoonShine\AssetManager\Js;protected function onLoad(): void{$this->getAssetManager()->add(Css::make('/css/app.css'))->append(Js::make('/js/app.js'));}
Response модификаторы
Если ресурс в режиме "async", то вы можете модифицировать ответ:
use Symfony\Component\HttpFoundation\Response;use MoonShine\Crud\JsonResponse;public function modifyDestroyResponse(JsonResponse $response): JsonResponse{return $response;}public function modifyMassDeleteResponse(JsonResponse $response): JsonResponse{return $response;}public function modifySaveResponse(JsonResponse $response): JsonResponse{return $response;}public function modifyErrorResponse(Response $response, Throwable $exception): Response{return $response;}
use Symfony\Component\HttpFoundation\Response;use MoonShine\Crud\JsonResponse;public function modifyDestroyResponse(JsonResponse $response): JsonResponse{return $response;}public function modifyMassDeleteResponse(JsonResponse $response): JsonResponse{return $response;}public function modifySaveResponse(JsonResponse $response): JsonResponse{return $response;}public function modifyErrorResponse(Response $response, Throwable $exception): Response{return $response;}
Обработчики CRUD-операций
Вы можете изменить логику операций сохранения, удаления и массового удаления записей в ModelResource
с помощью своих обработчиков и атрибутов SaveHandler, DestroyHandler и MassDestroyHandler.
В обработчик операции сохранения прокидывается массив $data, который уже прошёл через метод apply() у полей формы.
Пример использования:
use MoonShine\Crud\Attributes\DestroyHandler;use MoonShine\Crud\Attributes\MassDestroyHandler;use MoonShine\Crud\Attributes\SaveHandler;#[DestroyHandler(MoonShineUserRoleHandlers::class, 'destroy')]#[MassDestroyHandler(MoonShineUserRoleHandlers::class, 'massDestroy')]#[SaveHandler(MoonShineUserRoleHandlers::class, 'save')]class MoonShineUserRoleResource extends ModelResource{// ...}
use MoonShine\Crud\Attributes\DestroyHandler;use MoonShine\Crud\Attributes\MassDestroyHandler;use MoonShine\Crud\Attributes\SaveHandler;#[DestroyHandler(MoonShineUserRoleHandlers::class, 'destroy')]#[MassDestroyHandler(MoonShineUserRoleHandlers::class, 'massDestroy')]#[SaveHandler(MoonShineUserRoleHandlers::class, 'save')]class MoonShineUserRoleResource extends ModelResource{// ...}
Класс с методами для обработки операций может выглядеть так:
final readonly class MoonShineUserRoleHandlers{public function save(MoonshineUserRole $model, array $data): MoonshineUserRole{$model->fill($data);$model->save();return $model;}public function destroy(MoonshineUserRole $model): bool{return $model->delete();}public function massDestroy(array $ids): void{foreach ($ids as $id) {MoonshineUserRole::query()->whereKey($id)->delete();}}}final readonly class MoonShineUserRoleHandlers{public function save(MoonshineUserRole $model, array $data): MoonshineUserRole{$model->fill($data);$model->save();return $model;}public function destroy(MoonshineUserRole $model): bool{return $model->delete();}public function massDestroy(array $ids): void{foreach ($ids as $id) {MoonshineUserRole::query()->whereKey($id)->delete();}}}
Вы также можете использовать классы-обработчики вместо методов — в этом случае они должны реализовывать метод __invoke():
use MoonShine\Crud\Attributes\DestroyHandler;use MoonShine\Crud\Attributes\MassDestroyHandler;use MoonShine\Crud\Attributes\SaveHandler;#[SaveHandler(MoonShineUserRoleSaveHandler::class)]#[DestroyHandler(MoonShineUserRoleDestroyHandler::class)]#[MassDestroyHandler(MoonShineUserRoleMassDestroyHandler::class)]class MoonShineUserRoleResource extends ModelResource{// ...}
use MoonShine\Crud\Attributes\DestroyHandler;use MoonShine\Crud\Attributes\MassDestroyHandler;use MoonShine\Crud\Attributes\SaveHandler;#[SaveHandler(MoonShineUserRoleSaveHandler::class)]#[DestroyHandler(MoonShineUserRoleDestroyHandler::class)]#[MassDestroyHandler(MoonShineUserRoleMassDestroyHandler::class)]class MoonShineUserRoleResource extends ModelResource{// ...}
final readonly class MoonShineUserRoleSaveHandler{public function __invoke(MoonshineUserRole $model, array $data): MoonshineUserRole{$model->fill($data);$model->save();return $model;}}final readonly class MoonShineUserRoleDestroyHandler{public function __invoke(MoonshineUserRole $model): bool{return $model->delete();}}final readonly class MoonShineUserRoleMassDestroyHandler{public function __invoke(array $ids): void{foreach ($ids as $id) {MoonshineUserRole::query()->whereKey($id)->delete();}}}final readonly class MoonShineUserRoleSaveHandler{public function __invoke(MoonshineUserRole $model, array $data): MoonshineUserRole{$model->fill($data);$model->save();return $model;}}final readonly class MoonShineUserRoleDestroyHandler{public function __invoke(MoonshineUserRole $model): bool{return $model->delete();}}final readonly class MoonShineUserRoleMassDestroyHandler{public function __invoke(array $ids): void{foreach ($ids as $id) {MoonshineUserRole::query()->whereKey($id)->delete();}}}