# Basics
At any admin panel heart are sections for editing data.
MoonShine is no exception to this
and uses Eloquent
models to work with the database,
and for sections there are standard Laravel resource controllers and resource routes.
If you were developing on your own, then create resource controllers and resource routes can be as follows:
php artisan make:controller Controller --resource
Route::resource('resources', Controller::class);
However, this work can be entrusted to the MoonShine admin panel, which will generate and declare them independently.
ModelResource
is the main component for creating a section
in the admin panel when working with the database.
# Creating a section
php artisan moonshine:resource Post
- change your resource name if required<
- select resource type
There are several options available when creating a ModelResource:
- Default model resource - model resource with common fields
- Separate model resource - model resource with field separation
- Model resource with pages - model resource with pages.
As a result, a PostResource
class will be created, which will be a new section basis in the panel.
It is located, by default, in the app/MoonShine/Resources
directory.
MoonShine will automatically, based on the name, link the resource to the app/Models/Post
model.
The section title will also be generated automatically and will be “Posts”.
You can immediately specify the model binding and section title for the command:
php artisan moonshine:resource Post --model=CustomPost --title="Articles"
php artisan moonshine:resource Post --model="App\Models\CustomPost" --title="Articles"
# Basic section properties
Basic parameters that can be changed for a resource to customize its operation
namespace App\MoonShine\Resources; use App\Models\Post;use MoonShine\Resources\ModelResource; class PostResource extends ModelResource{ protected string $model = Post::class; // Model protected string $title = 'Posts'; // Section title protected array $with = ['category']; // Eager load protected string $column = 'id'; // Field to display values in links and breadcrumbs //...}
# Declaring a section in the system
Register the resource in the system and immediately add a link to the section in the navigation menu
you can use the service provider MoonShineServiceProvider
.
namespace App\Providers; use App\MoonShine\Resources\PostResource; use MoonShine\Providers\MoonShineApplicationServiceProvider; class MoonShineServiceProvider extends MoonShineApplicationServiceProvider{ //... protected function menu(): array { return [ MenuItem::make('Posts', new PostResource()) ]; } //...}
You can learn about advanced settings in the section
Menu
.
If you only need to register the resource in the system without adding it to the navigation menu:
namespace App\Providers; use App\MoonShine\Resources\PostResource; use MoonShine\Providers\MoonShineApplicationServiceProvider; class MoonShineServiceProvider extends MoonShineApplicationServiceProvider{ protected function resources(): array { return [ new PostResource() ]; } //...}
# Current element/model
If the url of the detail page or editing page contains the resourceItem
parameter,
then in a resource you can access the current item through the getItem()
method.
$this->getItem();
You can access the model through the getModel()
method.
$this->getModel();
# Modal windows
Ability to add, edit and view entries directly on the list page in a modal window.
namespace App\MoonShine\Resources; use App\Models\Post;use MoonShine\Resources\ModelResource; class PostResource extends ModelResource{ protected string $model = Post::class; protected string $title = 'Posts'; protected bool $createInModal = false; protected bool $editInModal = false; protected bool $detailInModal = false; //...}
# Redirects
By default, when creating and editing a record, a redirect is made to the page with the form, but this behaviour can be controlled
// Via a property in a resourceprotected ?PageType $redirectAfterSave = PageType::FORM; // or through methods (redirect after deletion is also available) public function redirectAfterSave(): string{ return '/';} public function redirectAfterDelete(): string{ return to_page(CustomPage::class);}
# Active actions
It often happens that it is necessary to create a resource in which the ability to delete will be excluded,
or add or edit. In addition, here we are not talking about authorization, but about the global exclusion of these sections.
This is done extremely simply using the getActiveActions
method in the resource
namespace MoonShine\Resources; class PostResource extends ModelResource{ //... public function getActiveActions(): array { return ['create', 'view', 'update', 'delete', 'massDelete']; } //...}
# Buttons
By default, the model resource index page only has a button to create.
The actions()
method allows you to add additional
buttons
.
namespace MoonShine\Resources; class PostResource extends ModelResource{ //... public function actions(): array { return [ ActionButton::make('Refresh', '#') ->dispatchEvent(AlpineJs::event(JsEvent::TABLE_UPDATED, 'index-table')) ]; } //...}
You can also change the display of the buttons, display them in a line or in a drop-down menu to save space.
namespace MoonShine\Resources; class PostResource extends ModelResource{ //... public function actions(): array { return [ ActionButton::make('Button 1', '/') ->showInLine(), ActionButton::make('Button 2', '/') ->showInDropdown() ]; } //...}
# Modification
To modify the main IndexPage, FormPage or DetailPage component
pages from a resource, you can override the corresponding modifyListComponent()
,
modifyFormComponent()
and modifyDetailComponent()
methods.
public function modifyListComponent(MoonShineRenderable $component): MoonShineRenderable{ return parent::modifyListComponent($component)->customAttributes([ 'data-my-attr' => 'value' ]);}
public function modifyFormComponent(MoonShineRenderable $component): MoonShineRenderable{ return parent::modifyFormComponent($component)->fields([ FlexibleRender::make('Top'), ...parent::modifyFormComponent($component)->getFields()->toArray(), FlexibleRender::make('Bottom'), ])->submit('Go');}
public function modifyDetailComponent(MoonShineRenderable $component): MoonShineRenderable{ return parent::modifyDetailComponent($component)->customAttributes([ 'data-my-attr' => 'value' ]);}
# Components
The best way to change page components is to publish the pages
and interact through them, but if you want to quickly add components to pages,
then you can use the methods of the pageComponents
resource,
indexPageComponents
,
formPageComponents
,
detailPageComponents
// or indexPageComponents/formPageComponents/detailPageComponentspublic function pageComponents(): array{ return [ Modal::make( 'My Modal' components: PageComponents::make([ FormBuilder::make()->fields([ Text::make('Title') ]) ]) ) ->name('demo-modal') ];}
The components will be added to the bottomLayer
# Boot
If you need to add logic to a resource operation when it is active and loaded,
then use the onBoot
method
namespace App\MoonShine\Resources; use App\Models\Post;use MoonShine\Resources\ModelResource; class PostResource extends ModelResource{ // ... protected function onBoot(): void { // } // ...}
Recipe: Changing breadcrumbs from a resource .
You can also connect trait to a resource and inside trait add a method according to the naming convention - boot{TraitName} and through the trait will access the boot resource
namespace App\MoonShine\Resources; use App\Models\Post;use MoonShine\Resources\ModelResource;use App\Traits\WithPermissions; class PostResource extends ModelResource{ use WithPermissions;}
trait WithPermissions{ protected function bootWithPermissions(): void { $this->getPages() ->findByUri(PageType::FORM->value) ->pushToLayer( layer: Layer::BOTTOM, component: Permissions::make( label: 'Permissions', resource: $this, ) ); }}