The in-app guide
This is the same guide as Admin → Help: usage, each admin screen, themes, tags, environment, and install.
How to write Blade inside a theme. Switching, copying, and uploading skins is under Themes. You can also open Edit templates on a copied theme. Pull entries with @cms* — see Tags. Do not query the database from a theme.
Folder layout
resources/views/themes/enterprise/
theme.json
preview.png
index/index.blade.php home (required for “Use on this site”)
list/list.blade.php section list
list/list_product.blade.php product list (optional)
show/show.blade.php entry
show/show_product.blade.php product (optional)
category/category.blade.php section cover
search.blade.php
tag.blade.php
sitemap.blade.php
sitemap-page.blade.php HTML sitemap for visitors (/sitemap)
partials/layout.blade.php chrome
partials/paginate.blade.php
member/login.blade.php plus register, profile, …
If a file is missing, the enterprise theme or resources/views/cms/ is used so the site is not blank.
How a template is chosen
List: section list template → model list_tpl → default list.list. Show: entry show_tpl → section → model → default show.show. Dots map to folders, so list.list_product is list/list_product.blade.php.
Layout prefers partials/layout.blade.php, then a root layout.blade.php.
Page skeleton
@extends('themes.enterprise.partials.layout')
@section('content')
@cmsBreadcrumb
<h1>{{ $category->t('title') }}</h1>
@cmsContent(['page' => true])
<a href="{{ $item->url }}">{{ $item->t('title') }}</a>
@endcmsContent
@cmsPaginate
@endsection
Put @cmsSeo in the head. Nav: @include('cms.partials.header-nav') or @cmsMenu.
Variables
| Variable | When | Typical use |
|---|---|---|
$site | Almost every front page | Name, logo, color |
$category | Section, list | $category->t('title'), frontUrl(), page_size |
$entry | Show | $entry->t('title'), t('content'), url, cover, views |
$item | Inside a tag loop | Default loop var; nest with 'as' => 'cat' |
Use t('title') for locales, not ->title. Extra fields: $item->attr('price'). Images: cms_media($item->cover) (honors CDN).
Make your own
- On Themes click “Copy one”, upload a zip with
index/index.blade.php, or drop a folder underthemes/. - Or:
php artisan cms:theme-make brand --from=enterprise --name=Brand. - Edit Blade and CSS. Models and empty sections go in
theme.json. “Use on this site” only fills gaps. - Preview:
preview.png(or jpg / svg).
{
"name": "Brand",
"description": "Company site",
"i18n": true,
"responsive": true,
"models": ["article", "page", "product"],
"config": {
"home_notice": {"title": "Home notice", "type": "textarea", "value": ""}
},
"categories": [
{"slug": "news", "title": {"zh_cn": "新闻中心", "en": "News"}, "model": "article", "type": "list", "menu": true}
]
}
No executable PHP in the zip. Custom model JSON can live in models/slug.json (same as “Export model”). Object titles are written per language on apply. Keep viewport, the language switcher, and cms_ui() in the layout.
Extend with events, not core edits
Saving entries, comments, forms, switching themes, and writing settings dispatch Laravel events. Listen in app/Providers/AppServiceProvider.php with Event::listen. The same table is in CONTRIBUTING.md.
| Event | When |
|---|---|
App\Events\EntrySaved | An entry was saved. $created is a new row; $publishedNow means it became published this save (member drafts do not set that) |
App\Events\CommentSubmitted | A front comment was posted (including pending) |
App\Events\CommentModerated | Admin approved / rejected / marked spam |
App\Events\FormSubmitted | A front form POST succeeded |
App\Events\ThemeActivated | The current site’s theme changed ($slug / $previous) |
App\Events\SettingSaved | Settings keys were written ($keys) |
use App\Events\EntrySaved;
use Illuminate\Support\Facades\Event;
Event::listen(EntrySaved::class, function (EntrySaved $e) {
if ($e->publishedNow) {
// ping a search engine, write a log, …
}
});
Bulk status changes on the entry list do not fire EntrySaved, so you do not mail hundreds of times. Do not fork controllers for “when an article is published”.