Skip to content

Pages ​

The Pages screen (/admin/pages, every role) holds the pages of the site. The core provides the model, the admin screen and the route, so a site only writes the views.

FieldDetail
Title, address, textOne per site language. The address is written from the title and can be changed. It may have levels (services/web-design)
Published onEmpty: a draft. A future date: public from that date
TemplateOne of the site's views in resources/views/pages. Shown once the site has more than one
Cover imageA file of the media library
Search enginesTitle, description and share image for search engines and social networks. See SEO

How a page reaches the site ​

The core answers every address that no route of the site matches. It looks for the published page at that address in the language of the request, and shows it with the view of its template:

RequestPage shown
/aboutThe page whose address is about in the default language
/it/chi-siamoThe page whose Italian address is chi-siamo
/it/aboutThe page whose address is about in the default language, when it has no Italian address
/The page chosen as home page on the Settings page

A language without an address uses the one of the default language, then that of the next site language that has one. So a page keeps its address when the default language changes.

A route of the site always wins. The Pages screen refuses an address that a route of the site already answers, and a route added later still wins over a page saved before it.

The home page

A fresh Laravel app has a route for / in routes/web.php. Remove it, so the home page chosen on the Settings page is served.

A draft answers with a 404, except for a logged-in user who may edit pages.

Templates ​

Until the site has its own view, the core shows pages with a plain one. Create resources/views/pages/default.blade.php to give the pages the look of the site:

blade
<x-layout :title="$page->title">
    <h1>{{ $page->title }}</h1>

    @if ($page->cover)
        <img src="{{ $page->cover->url('large') }}" alt="{{ $page->cover->alt }}">
    @endif

    @richtext($page->body)
</x-layout>

Every other file in that directory is a template an editor can choose: resources/views/pages/landing-page.blade.php is offered as "Landing Page". A page whose template was removed is shown with default.

In a viewReturns
$page->title, $page->slugIn the language of the request, with the default language as fallback
@richtext($page->body)The text as sanitized HTML
$page->coverThe cover image, a Media record, or null
$page->url(), $page->url('it')The full address of the page
$page->isHome()Whether it is the home page
Page::published()->get()The pages visitors may see, for example to build a menu

Print the text with @richtext, not with {!! !!}. It removes anything that could run in the browser, and reads the address of each image again from the media disk.

Fields for one template ​

A template that needs more than a title and a text gets its fields from the site. Extend the resource and add them under fields:

php
namespace App\Filament\Resources;

use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Melasistema\MelaCms\Filament\Forms\Translations;
use Melasistema\MelaCms\Filament\Resources\Pages\PageResource as CorePageResource;

class PageResource extends CorePageResource
{
    public static function formComponents(): array
    {
        return [
            ...parent::formComponents(),
            Section::make('Hero')
                ->visible(fn (Get $get): bool => $get('template') === 'home')
                ->schema([
                    TextInput::make('fields.hero.link'),
                    Translations::make([TextInput::make('fields.hero.button')]),
                ]),
        ];
    }
}
php
// config/mela-cms.php
'resources' => [
    'pages' => \App\Filament\Resources\PageResource::class,
],
blade
<a href="{{ $page->field('hero.link') }}">{{ $page->translated('hero.button') }}</a>

The values are saved in one JSON column, so no migration is needed.

Serving pages yourself ​

Set 'public_pages' => false in the config, and find a page with Page::published()->whereSlug($slug)->firstOrFail(). A Route::fallback() of the site also replaces the core's.

Known limits ​

  • No revisions or autosave.
  • No menu builder. A site builds its menu from Page::published().
  • Changing an address leaves no redirect from the old one.
  • An image in a text breaks if its file is deleted from the library.

Code released under the MIT License. The MelaCMS name and logo are trademarks, see Brand.