Pages

Pages

Basics

Page is the foundation of the MoonShine admin panel. The main purpose of Page is to display components.

Pages with similar logic can be combined into a Resource.

Creating a Page

To create a page class, you can use the console command:

php artisan moonshine:page
php artisan moonshine:page

After entering the class name, a file will be created that serves as the basis for the page in the admin panel. By default, it is located in the app/MoonShine/Pages directory.

You can learn about all supported options in the section Commands.

Pages are automatically registered in the system when the command is executed, but if you create a page manually, it must be registered in the MoonShineServiceProvider in the $core->pages() method.

Title

The page title can be set through the $title property, and the subtitle can be set through $subtitle.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected string $title = 'CustomPage';
 
protected string $subtitle = 'Subtitle';
 
// ...
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected string $title = 'CustomPage';
 
protected string $subtitle = 'Subtitle';
 
// ...
}

If some logic is required for the title and subtitle, the title() and subtitle() methods allow you to implement it.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
// ...
 
public function getTitle(): string
{
return $this->title ?: 'CustomPage';
}
 
public function getSubtitle(): string
{
return $this->subtitle ?: 'Subtitle';
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
// ...
 
public function getTitle(): string
{
return $this->title ?: 'CustomPage';
}
 
public function getSubtitle(): string
{
return $this->subtitle ?: 'Subtitle';
}
}

Components

To register the components of the page, the components() method is used.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
use MoonShine\UI\Components\Layout\Box;
use MoonShine\UI\Components\Layout\Column;
use MoonShine\UI\Components\Layout\Grid;
 
class CustomPage extends Page
{
// ...
 
protected function components(): iterable
{
return [
Grid::make([
Column::make([
Box::make([
// ...
])
])->columnSpan(6),
Column::make([
Box::make([
// ...
])
])->columnSpan(6),
])
];
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
use MoonShine\UI\Components\Layout\Box;
use MoonShine\UI\Components\Layout\Column;
use MoonShine\UI\Components\Layout\Grid;
 
class CustomPage extends Page
{
// ...
 
protected function components(): iterable
{
return [
Grid::make([
Column::make([
Box::make([
// ...
])
])->columnSpan(6),
Column::make([
Box::make([
// ...
])
])->columnSpan(6),
])
];
}
}

For more detailed information, refer to the Components section.

The getBreadcrumbs() method is responsible for generating the breadcrumbs.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
// ...
 
public function getBreadcrumbs(): array
{
return [
'#' => $this->getTitle()
];
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
// ...
 
public function getBreadcrumbs(): array
{
return [
'#' => $this->getTitle()
];
}
}

Layout

By default, pages use the AppLayout or CompactLayout display template. For more information about templates, see the Layout section.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected ?string $layout = AppLayout::class;
 
// ...
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected ?string $layout = AppLayout::class;
 
// ...
}

Modifying Layout

When developing an admin panel using MoonShine, there is often a need for flexible management of templates. Instead of creating numerous separate templates for various situations, MoonShine provides an opportunity to dynamically modify existing templates. This is achieved through the modifyLayout() method.

The modifyLayout() method allows you to access the template after creating its instance and make necessary changes. This is especially useful when you need to adapt the template to specific conditions or add dynamic content.

Example usage

Consider an example from the moonshine-software/two-factor package that demonstrates how to use modifyLayout() for customizing the authentication template:

 namespaces
use MoonShine\Contracts\UI\LayoutContract;
 
/**
* @param LoginLayout $layout
*/
protected function modifyLayout(LayoutContract $layout): LayoutContract
{
return $layout->title(
__('moonshine-two-factor::ui.2fa')
)->description(
__('moonshine-two-factor::ui.confirm')
);
}
 namespaces
use MoonShine\Contracts\UI\LayoutContract;
 
/**
* @param LoginLayout $layout
*/
protected function modifyLayout(LayoutContract $layout): LayoutContract
{
return $layout->title(
__('moonshine-two-factor::ui.2fa')
)->description(
__('moonshine-two-factor::ui.confirm')
);
}

Alias

If you need to change the page alias, this can be done through the $alias property.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected ?string $alias = null;
 
// ...
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected ?string $alias = null;
 
// ...
}

You can also override the getAlias() method.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
public function getAlias(): ?string
{
return 'custom_page';
}
 
// ...
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
public function getAlias(): ?string
{
return 'custom_page';
}
 
// ...
}

Rendering

You can display the page outside MoonShine by simply returning it in a controller.

class ProfileController extends Controller
{
// ...
 
public function __invoke(ProfilePage $page): ProfilePage
{
return $page->loaded();
}
}
class ProfileController extends Controller
{
// ...
 
public function __invoke(ProfilePage $page): ProfilePage
{
return $page->loaded();
}
}

Or with Fortify

Fortify::loginView(static fn() => app(ProfilePage::class));
Fortify::loginView(static fn() => app(ProfilePage::class));

Before Rendering

The prepareBeforeRender() method allows you to execute actions before displaying the page.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected function prepareBeforeRender(): void
{
parent::prepareBeforeRender();
 
if (auth()->user()->moonshine_user_role_id !== MoonshineUserRole::DEFAULT_ROLE_ID) {
abort(403);
}
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected function prepareBeforeRender(): void
{
parent::prepareBeforeRender();
 
if (auth()->user()->moonshine_user_role_id !== MoonshineUserRole::DEFAULT_ROLE_ID) {
abort(403);
}
}
}

Response Modifier

By default, the page is rendered through the PageController, invoking the render() method. However, there are times when it's necessary to change the standard response, such as redirecting under certain conditions. In such cases, the modifyResponse() method can be used.

The modifyResponse() method allows you to modify the page response before it is sent. Here’s an example of its usage:

 namespaces
use Symfony\Component\HttpFoundation\Response;
 
protected function modifyResponse(): ?Response
{
if (request()->has('id')) {
return redirect()->to('/admin/article-resource/index-page');
}
 
return null;
}
 namespaces
use Symfony\Component\HttpFoundation\Response;
 
protected function modifyResponse(): ?Response
{
if (request()->has('id')) {
return redirect()->to('/admin/article-resource/index-page');
}
 
return null;
}

Using modifyResponse() provides a flexible way to manage the page response, allowing for complex request and response handling logic in the admin panel.

Lifecycle

Page has several different methods to hook into various parts of its lifecycle. Let’s go through them:

Active Page

The onLoad() method allows integration at the moment when the page is loaded and is currently active.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class PostPage extends Page
{
// ...
 
protected function onLoad(): void
{
parent::onLoad();
 
// ...
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class PostPage extends Page
{
// ...
 
protected function onLoad(): void
{
parent::onLoad();
 
// ...
}
}

Booting Instance

The booted() method allows integration at the moment when MoonShine creates an instance of the page in the system.

 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class PostPage extends Page
{
// ...
 
protected function booted(): void
{
parent::booted();
 
// ...
}
}
 namespaces
namespace App\MoonShine\Pages;
 
use MoonShine\Laravel\Pages\Page;
 
class PostPage extends Page
{
// ...
 
protected function booted(): void
{
parent::booted();
 
// ...
}
}

In this example, to create a link to a new page, we'll use the ActionButton and the getPageUrl method.

 namespaces
use MoonShine\Support\ListOf;
use MoonShine\UI\Components\ActionButton;
 
/**
* @throws Throwable
*/
public function indexButtons(): ListOf
{
return parent::indexButtons()->add(
ActionButton::make('To custom page',
url: fn($model) => $this->getPageUrl(
PostPage::class, params: ['resourceItem' => $model->getKey()]
),
),
);
}
 namespaces
use MoonShine\Support\ListOf;
use MoonShine\UI\Components\ActionButton;
 
/**
* @throws Throwable
*/
public function indexButtons(): ListOf
{
return parent::indexButtons()->add(
ActionButton::make('To custom page',
url: fn($model) => $this->getPageUrl(
PostPage::class, params: ['resourceItem' => $model->getKey()]
),
),
);
}

Assets

 namespaces
use MoonShine\AssetManager\Css;
use MoonShine\AssetManager\Js;
 
protected function onLoad(): void
{
parent::onLoad();
 
$this->getAssetManager()
->add(Css::make('/css/app.css'))
->append(Js::make('/js/app.js'));
}
 namespaces
use MoonShine\AssetManager\Css;
use MoonShine\AssetManager\Js;
 
protected function onLoad(): void
{
parent::onLoad();
 
$this->getAssetManager()
->add(Css::make('/css/app.css'))
->append(Js::make('/js/app.js'));
}