Models Resources

Basics

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
php artisan make:controller Controller --resource
Route::resource('resources', Controller::class);
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
php artisan moonshine:resource Post
  • change your resource name if required
  • select resource type

There are several options available when creating a ModelResource:

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=CustomPost --title="Articles"
php artisan moonshine:resource Post --model="App\Models\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 //... }
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
 
//...
}

resource_paginate resource_paginate_dark

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()) ]; } //... }
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() ]; } //... }
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();
$this->getItem();

You can access the model through the getModel() method.

$this->getModel();
$this->getModel();

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; //... }
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 resource protected ?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); }
// Via a property in a resource
protected ?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']; } //... }
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')) ]; } //... }
namespace MoonShine\Resources;
 
class PostResource extends ModelResource
{
//...
 
public function actions(): array
{
return [
ActionButton::make('Refresh', '#')
->dispatchEvent(AlpineJs::event(JsEvent::TABLE_UPDATED, 'index-table'))
];
}
 
//...
}

Display

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() ]; } //... }
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 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 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' ]); }
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/detailPageComponents public function pageComponents(): array { return [ Modal::make( 'My Modal' components: PageComponents::make([ FormBuilder::make()->fields([ Text::make('Title') ]) ]) ) ->name('demo-modal') ]; }
// or indexPageComponents/formPageComponents/detailPageComponents
public 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 { // } // ... }
namespace App\MoonShine\Resources;
 
use App\Models\Post;
use MoonShine\Resources\ModelResource;
 
class PostResource extends ModelResource
{
// ...
protected function onBoot(): void
{
//
}
// ...
}

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; }
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, ) ); } }
trait WithPermissions
{
protected function bootWithPermissions(): void
{
$this->getPages()
->findByUri(PageType::FORM->value)
->pushToLayer(
layer: Layer::BOTTOM,
component: Permissions::make(
label: 'Permissions',
resource: $this,
)
);
}
}