ModelResource

Основы

Основы

ModelResource расширяет CrudResource и предоставляет функциональность для работы с моделями Eloquent. Он обеспечивает основу для создания ресурсов, связанных с моделями базы данных. ModelResource предоставляет методы для выполнения CRUD-операций, управления отношениями, применения фильтров и многое другое.

Вы также можете ознакомиться с разделом CrudResource. CrudResource - это абстрактный класс предоставляющий базовый интерфейс для CRUD операций без привязки к хранилищу и типу данных.

Под капотом, ModelResource расширяет CrudResource и сразу включает возможность работы с Eloquent. Если углубляться в детали MoonShine, то вы увидите все те же стандартные Controller, Model и blade views.

Если бы вы разрабатывали самостоятельно, то создать ресурс контроллеры и ресурс маршруты можно следующим образом:

php artisan make:controller Controller --resource
 namespaces
use Illuminate\Support\Facades\Route;
 
Route::resource('resources', Controller::class);

Но эту работу можно поручить админ-панели MoonShine, которая будет их генерировать и объявлять самостоятельно.

ModelResource является основным компонентом для создания раздела в админ-панели при работе с базой данных.

Создание

php artisan moonshine:resource Post

Для более подробной информации обратитесь к разделу Команды.

Базовые свойства

Базовые параметры, которые можно менять у ресурса, чтобы кастомизировать его работу.

 namespaces
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 load
protected array $with = ['category'];
 
// Поле для отображения значений в связях и хлебных крошках
protected string $column = 'id';
 
// ...
}

resource_paginate resource_paginate_dark

Объявление в системе

Ресурс автоматически регистрируется в MoonShineServiceProvider при вызове команды php artisan moonshine:resource. Но если вы создаете раздел вручную, то вам необходимо самостоятельно его объявить в системе в MoonShineServiceProvider.

 namespaces
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;
 
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 вместо указания ссылок на страницы и ресурсы.

 namespaces
namespace App\Providers;
 
use App\MoonShine\Resources\ArticleResource;
 
use Illuminate\Support\ServiceProvider;
use MoonShine\Contracts\Core\DependencyInjection\CoreContract;
use MoonShine\Laravel\DependencyInjection\ConfiguratorContract;
 
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.

 namespaces
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;
 
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().

 namespaces
namespace App\MoonShine\Resources;
 
use MoonShine\Laravel\Resources\ModelResource;
 
class PostResource extends ModelResource
{
protected ?string $alias = 'custom-alias';
 
// ...
}

или

 namespaces
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();

Через метод getModel() можно получить доступ к модели.

$this->getModel();

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

 namespaces
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;
 
// ...
}

Редиректы

По умолчанию при создании и редактировании записи осуществляется редирект на страницу с формой, но это поведение можно контролировать.

 namespaces
use MoonShine\Support\Enums\PageType;
 
// Через свойство в ресурсе
protected ?PageType $redirectAfterSave = PageType::FORM;
 
// или через методы (также доступен редирект после удаления)
 
public function getRedirectAfterSave(): string
{
return '/';
}
 
public function getRedirectAfterDelete(): string
{
return $this->getIndexPageUrl();
}

Активные действия

Часто бывает, что необходимо создать ресурс, в котором будет исключена возможность удалять, или добавлять, или редактировать. И здесь речь не об авторизации, а о глобальном исключении этих разделов. Делается это крайне просто за счет метода activeActions() в ресурсе.

 namespaces
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)
;
}
}

Также можно просто создать новый список, например:

 namespaces
use MoonShine\Laravel\Enums\Action;
use MoonShine\Support\ListOf;
 
protected function activeActions(): ListOf
{
return new ListOf(Action::class, [Action::VIEW, Action::UPDATE]);
}

Кнопки

По умолчанию на индексной странице ресурса модели присутствует только кнопка для создания. Метод topButtons() позволяет добавить дополнительные кнопки.

 namespaces
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;
 
class PostResource extends ModelResource
{
// ...
 
protected function topButtons(): ListOf
{
return parent::topButtons()->add(
ActionButton::make('Refresh', '#')
->dispatchEvent(AlpineJs::event(JsEvent::TABLE_UPDATED, $this->getListComponentName()))
);
}
}

Отображение

Вы также можете изменить отображение кнопок, отображать их в линию или же в выпадающем меню для экономии места.

 namespaces
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().

 namespaces
use MoonShine\Contracts\UI\ComponentContract;
 
public function modifyListComponent(ComponentContract $component): ComponentContract
{
return parent::modifyListComponent($component)->customAttributes([
'data-my-attr' => 'value'
]);
}
 namespaces
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');
}
 namespaces
use MoonShine\Contracts\UI\ComponentContract;
 
public function modifyDetailComponent(ComponentContract $component): ComponentContract
{
return parent::modifyDetailComponent($component)->customAttributes([
'data-my-attr' => 'value'
]);
}

Компоненты

Лучший способ изменить компоненты страниц - это опубликовать страницы и взаимодействовать через них. Но, если вы хотите быстро добавить компоненты на страницы, то можете воспользоваться методами ресурса pageComponents(), indexPageComponents(), formPageComponents() и detailPageComponents().

 namespaces
use MoonShine\Core\Collections\Components;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Components\Modal;
use MoonShine\UI\Fields\Text;
 
// or indexPageComponents/formPageComponents/detailPageComponents
protected function pageComponents(): array
{
return [
Modal::make(
'My Modal'
components: Components::make([
FormBuilder::make()->fields([
Text::make('Title')
])
])
)
->name('demo-modal')
];
}

Компоненты будут добавлены в bottomLayer.

Жизненный цикл

Resource имеет несколько различных методов подключения к различным частям своего жизненного цикла. Давайте пройдемся по ним:

Активный ресурс

Метод onLoad() дает возможность интегрироваться в момент когда ресурс загружен и в данный момент является активным.

 namespaces
namespace App\MoonShine\Resources;
 
use MoonShine\Laravel\Resources\ModelResource;
 
class PostResource extends ModelResource
{
// ...
 
protected function onLoad(): void
{
// ...
}
}

Вы также можете подключить trait к ресурсу и внутри trait добавить метод согласно конвенции наименований - load{TraitName} и через трейт обратиться к onLoad ресурса.

 namespaces
namespace App\MoonShine\Resources;
 
use App\Traits\WithPermissions;
use MoonShine\Laravel\Resources\ModelResource;
 
class PostResource extends ModelResource
{
use WithPermissions;
 
// ...
}
 namespaces
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 создает экземпляр ресурса в системе.

 namespaces
namespace App\MoonShine\Resources;
 
use MoonShine\Laravel\Resources\ModelResource;
 
class PostResource extends ModelResource
{
// ...
 
protected function onBoot(): void
{
// ...
}
}

Вы также можете подключить trait к ресурсу и внутри trait добавить метод согласно конвенции наименований - boot{TraitName} и через трейт обратиться к onBoot() ресурса.

Assets

 namespaces
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, то вы можете модифицировать ответ:

public function modifyDestroyResponse(MoonShineJsonResponse $response): MoonShineJsonResponse
{
return $response;
}
 
public function modifyMassDeleteResponse(MoonShineJsonResponse $response): MoonShineJsonResponse
{
return $response;
}
 
public function modifySaveResponse(MoonShineJsonResponse $response): MoonShineJsonResponse
{
return $response;
}