- Основы
- Создание
- Базовые свойства
- Объявление в системе
- Автозагрузка
- Добавление в меню
- Текущий элемент/модель
- Модальные окна
- Редиректы
- Активные действия
- Кнопки
- Модификаторы
- Компоненты
- Жизненный цикл
- Assets
- Response модификаторы
Основы
ModelResource
расширяет CrudResource
и предоставляет функциональность для работы с моделями Eloquent.
Он обеспечивает основу для создания ресурсов, связанных с моделями базы данных.
ModelResource
предоставляет методы для выполнения CRUD-операций, управления отношениями, применения фильтров и многое другое.
Вы также можете ознакомиться с разделом CrudResource.
CrudResource
- это абстрактный класс предоставляющий базовый интерфейс для CRUD
операций без привязки к хранилищу и типу данных.
Под капотом, ModelResource
расширяет CrudResource
и сразу включает возможность работы с Eloquent.
Если углубляться в детали MoonShine, то вы увидите все те же стандартные Controller, Model и Blade views.
Если бы вы разрабатывали самостоятельно, то создать ресурс контроллеры и ресурс маршруты можно следующим образом:
php artisan make:controller Controller --resource
php artisan make:controller Controller --resource
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use Illuminate\Support\Facades\Route;Route::resource('resources', Controller::class);
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use Illuminate\Support\Facades\Route;Route::resource('resources', Controller::class);
Но эту работу можно поручить админ-панели MoonShine, которая будет их генерировать и объявлять самостоятельно.
ModelResource
является основным компонентом для создания раздела в админ-панели при работе с базой данных.
Создание
php artisan moonshine:resource Post
php artisan moonshine:resource Post
Для более подробной информации обратитесь к разделу Команды.
Базовые свойства
Базовые параметры, которые можно менять у ресурса, чтобы кастомизировать его работу.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]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';// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]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
.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\Providers;use App\MoonShine\Resources\ArticleResource;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\ConfiguratorContract;use MoonShine\Laravel\DependencyInjection\MoonShine;use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator; // [tl! collapse:end]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(),]);}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\Providers;use App\MoonShine\Resources\ArticleResource;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\ConfiguratorContract;use MoonShine\Laravel\DependencyInjection\MoonShine;use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator; // [tl! collapse:end]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
вместо указания ссылок на страницы и ресурсы.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\Providers;use App\MoonShine\Resources\ArticleResource;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\ConfiguratorContract; // [tl! collapse:end]class MoonShineServiceProvider extends ServiceProvider{public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->autoload();}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\Providers;use App\MoonShine\Resources\ArticleResource;use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\ConfiguratorContract; // [tl! collapse:end]class MoonShineServiceProvider extends ServiceProvider{public function boot(CoreContract $core,ConfiguratorContract $config,): void{$core->autoload();}}
При деплое проекта на продакшен в Laravel 11+ рекомендуется вызывать консольную команду php artisan optimize
.
Помимо её основных функций, она также выполнит кэширование ресурсов MoonShine.
При использовании Laravel 10 необходимо вручную вызывать консольную команду php artisan moonshine:optimize
для оптимизации процесса инициализации админ панели.
Очистить кэш панели можно как командой php artisan optimize:clear
в Laravel 11, так и прямым вызовом консольной команды php artisan moonshine:optimize-clear
.
Если после создания классов приложение их не видит - обновите кэш композера командой composer dump-autoload
.
Добавление в меню
Все страницы в MoonShine имеют Layout
и у каждой страницы он может быть свой.
По умолчанию при установке MoonShine добавляет базовый MoonShineLayout
в директорию app/MoonShine/Layouts
.
В Layout
кастомизируется всё, что отвечает за внешний вид ваших страниц и это касается также и навигации.
Чтобы добавить раздел в меню, необходимо объявить его через метод menu()
в Layout
.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\MoonShine\Layouts;use App\MoonShine\Resources\PostResource;use MoonShine\Laravel\Layouts\CompactLayout;use MoonShine\Laravel\Resources\MoonShineUserResource;use MoonShine\Laravel\Resources\MoonShineUserRoleResource;use MoonShine\MenuManager\MenuGroup;use MoonShine\MenuManager\MenuItem; // [tl! collapse:end]final class MoonShineLayout extends CompactLayout{// ...protected function menu(): array{return [MenuGroup::make(__('moonshine::ui.resource.system'), [MenuItem::make(__('moonshine::ui.resource.admins_title'),MoonShineUserResource::class),MenuItem::make(__('moonshine::ui.resource.role_title'),MoonShineUserRoleResource::class),]),MenuItem::make('Posts', PostResource::class),// ...];}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\MoonShine\Layouts;use App\MoonShine\Resources\PostResource;use MoonShine\Laravel\Layouts\CompactLayout;use MoonShine\Laravel\Resources\MoonShineUserResource;use MoonShine\Laravel\Resources\MoonShineUserRoleResource;use MoonShine\MenuManager\MenuGroup;use MoonShine\MenuManager\MenuItem; // [tl! collapse:end]final class MoonShineLayout extends CompactLayout{// ...protected function menu(): array{return [MenuGroup::make(__('moonshine::ui.resource.system'), [MenuItem::make(__('moonshine::ui.resource.admins_title'),MoonShineUserResource::class),MenuItem::make(__('moonshine::ui.resource.role_title'),MoonShineUserRoleResource::class),]),MenuItem::make('Posts', PostResource::class),// ...];}}
О расширенных настройках Layout
можно узнать в разделе Layout.
О расширенных настройках MenuManager
можно узнать в разделе Menu.
Alias
По умолчанию alias ресурса, который используется в url
, генерируется на основе наименования класс в kebab-case
, например:
MoonShineUserResource
-> moon-shine-user-resource
.
Для того чтобы изменить alias
, можно воспользоваться свойством ресурса $alias
или методом getAlias()
.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{protected ?string $alias = 'custom-alias';// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{protected ?string $alias = 'custom-alias';// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{public function getAlias(): ?string{return 'custom-alias';}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]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();
Модальные окна
Вы можете добавлять, редактировать и просматривать записи прямо на странице со списком в модальном окне.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{protected bool $createInModal = true;protected bool $editInModal = true;protected bool $detailInModal = true;// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{protected bool $createInModal = true;protected bool $editInModal = true;protected bool $detailInModal = true;// ...}
Редиректы
По умолчанию при создании и редактировании записи осуществляется редирект на страницу с формой, но это поведение можно контролировать.
Через свойство в ресурсе:
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use MoonShine\Support\Enums\PageType;protected ?PageType $redirectAfterSave = PageType::FORM;
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]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()
в ресурсе.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:5]namespace App\MoonShine\Resources;use MoonShine\Support\ListOf;use MoonShine\Laravel\Enums\Action;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function activeActions(): ListOf{return parent::activeActions()->except(Action::VIEW, Action::MASS_DELETE)// ->only(Action::VIEW);}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:5]namespace App\MoonShine\Resources;use MoonShine\Support\ListOf;use MoonShine\Laravel\Enums\Action;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function activeActions(): ListOf{return parent::activeActions()->except(Action::VIEW, Action::MASS_DELETE)// ->only(Action::VIEW);}}
Также можно просто создать новый список, например:
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]use MoonShine\Laravel\Enums\Action;use MoonShine\Support\ListOf;protected function activeActions(): ListOf{return new ListOf(Action::class, [Action::VIEW, Action::UPDATE]);}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]use MoonShine\Laravel\Enums\Action;use MoonShine\Support\ListOf;protected function activeActions(): ListOf{return new ListOf(Action::class, [Action::VIEW, Action::UPDATE]);}
Кнопки
По умолчанию на индексной странице ресурса модели присутствует только кнопка для создания.
Метод topButtons()
позволяет добавить дополнительные кнопки.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;use MoonShine\Support\AlpineJs;use MoonShine\Support\Enums\JsEvent;use MoonShine\Support\ListOf;use MoonShine\UI\Components\ActionButton; // [tl! collapse:end]class PostResource extends ModelResource{// ...protected function topButtons(): ListOf{return parent::topButtons()->add(ActionButton::make('Refresh', '#')->dispatchEvent(AlpineJs::event(JsEvent::TABLE_UPDATED, $this->getListComponentName())));}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:start]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;use MoonShine\Support\AlpineJs;use MoonShine\Support\Enums\JsEvent;use MoonShine\Support\ListOf;use MoonShine\UI\Components\ActionButton; // [tl! collapse:end]class PostResource extends ModelResource{// ...protected function topButtons(): ListOf{return parent::topButtons()->add(ActionButton::make('Refresh', '#')->dispatchEvent(AlpineJs::event(JsEvent::TABLE_UPDATED, $this->getListComponentName())));}}
Отображение
Вы также можете изменить отображение кнопок, отображать их в линию или же в выпадающем меню для экономии места.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]namespace App\MoonShine\Resources;use MoonShine\Support\ListOf;use MoonShine\UI\Components\ActionButton;class PostResource extends ModelResource{// ...protected function indexButtons(): ListOf{return parent::indexButtons()->prepend(ActionButton::make('Button 1', '/')->showInLine(),ActionButton::make('Button 2', '/')->showInDropdown(),);}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]namespace App\MoonShine\Resources;use MoonShine\Support\ListOf;use MoonShine\UI\Components\ActionButton;class PostResource extends ModelResource{// ...protected function indexButtons(): ListOf{return parent::indexButtons()->prepend(ActionButton::make('Button 1', '/')->showInLine(),ActionButton::make('Button 2', '/')->showInDropdown(),);}}
Модификаторы
Для модификации основного компонента IndexPage
, FormPage
или DetailPage
страницы из ресурса можно переопределить соответствующие методы modifyListComponent()
, modifyFormComponent()
и modifyDetailComponent()
.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use MoonShine\Contracts\UI\ComponentContract;public function modifyListComponent(ComponentContract $component): ComponentContract{return parent::modifyListComponent($component)->customAttributes(['data-my-attr' => 'value']);}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use MoonShine\Contracts\UI\ComponentContract;public function modifyListComponent(ComponentContract $component): ComponentContract{return parent::modifyListComponent($component)->customAttributes(['data-my-attr' => 'value']);}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]use MoonShine\Contracts\UI\ComponentContract;use MoonShine\UI\Components\FlexibleRender;public function modifyFormComponent(ComponentContract $component): ComponentContract{return parent::modifyFormComponent($component)->fields([FlexibleRender::make('Top'),...parent::modifyFormComponent($component)->getFields()->toArray(),FlexibleRender::make('Bottom'),])->submit('Go');}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]use MoonShine\Contracts\UI\ComponentContract;use MoonShine\UI\Components\FlexibleRender;public function modifyFormComponent(ComponentContract $component): ComponentContract{return parent::modifyFormComponent($component)->fields([FlexibleRender::make('Top'),...parent::modifyFormComponent($component)->getFields()->toArray(),FlexibleRender::make('Bottom'),])->submit('Go');}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use MoonShine\Contracts\UI\ComponentContract;public function modifyDetailComponent(ComponentContract $component): ComponentContract{return parent::modifyDetailComponent($component)->customAttributes(['data-my-attr' => 'value']);}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:1]use MoonShine\Contracts\UI\ComponentContract;public function modifyDetailComponent(ComponentContract $component): ComponentContract{return parent::modifyDetailComponent($component)->customAttributes(['data-my-attr' => 'value']);}
Компоненты
Лучший способ изменить компоненты страниц - это опубликовать страницы и взаимодействовать через них.
Но, если вы хотите быстро добавить компоненты на страницы, то можете воспользоваться методами ресурса pageComponents()
, indexPageComponents()
, formPageComponents()
и detailPageComponents()
.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]use MoonShine\Core\Collections\Components;use MoonShine\UI\Components\FormBuilder;use MoonShine\UI\Components\Modal;use MoonShine\UI\Fields\Text;// or indexPageComponents/formPageComponents/detailPageComponentsprotected function pageComponents(): array{return [Modal::make('My Modal'components: Components::make([FormBuilder::make()->fields([Text::make('Title')])]))->name('demo-modal')];}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]use MoonShine\Core\Collections\Components;use MoonShine\UI\Components\FormBuilder;use MoonShine\UI\Components\Modal;use MoonShine\UI\Fields\Text;// or indexPageComponents/formPageComponents/detailPageComponentsprotected function pageComponents(): array{return [Modal::make('My Modal'components: Components::make([FormBuilder::make()->fields([Text::make('Title')])]))->name('demo-modal')];}
Компоненты будут добавлены в bottomLayer
.
Жизненный цикл
Resource
имеет несколько различных методов подключения к различным частям своего жизненного цикла. Давайте пройдемся по ним:
Активный ресурс
Метод onLoad()
дает возможность интегрироваться в момент когда ресурс загружен и в данный момент является активным.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function onLoad(): void{// ...}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function onLoad(): void{// ...}}
Рецепт: Изменение breadcrumbs из ресурса.
Вы также можете подключить trait
к ресурсу и внутри trait
добавить метод согласно конвенции наименований - load{TraitName}
и через трейт обратиться к onLoad
ресурса.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]namespace App\MoonShine\Resources;use App\Traits\WithPermissions;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{use WithPermissions;// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:4]namespace App\MoonShine\Resources;use App\Traits\WithPermissions;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{use WithPermissions;// ...}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]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,));}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]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 создает экземпляр ресурса в системе.
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function onBoot(): void{// ...}}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:3]namespace App\MoonShine\Resources;use MoonShine\Laravel\Resources\ModelResource;class PostResource extends ModelResource{// ...protected function onBoot(): void{// ...}}
Вы также можете подключить trait
к ресурсу и внутри trait
добавить метод согласно конвенции наименований - boot{TraitName}
и через трейт обратиться к onBoot()
ресурса.
Assets
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]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'));}
// torchlight! {"summaryCollapsedIndicator": "namespaces"}// [tl! collapse:2]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\Laravel\Http\Responses\MoonShineJsonResponse;public function modifyDestroyResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifyMassDeleteResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifySaveResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifyErrorResponse(Response $response, Throwable $exception): Response{return $response;}
use Symfony\Component\HttpFoundation\Response;use MoonShine\Laravel\Http\Responses\MoonShineJsonResponse;public function modifyDestroyResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifyMassDeleteResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifySaveResponse(MoonShineJsonResponse $response): MoonShineJsonResponse{return $response;}public function modifyErrorResponse(Response $response, Throwable $exception): Response{return $response;}