Purpose
A frozen view of one page that themes consume from inside a
template. Wraps an Imanager\Domain\Item from the Pages category
and re-projects the bag of fields as typed PHP properties (name,
slug, template, parent, pagetype, content, menu_title)
plus a small set of accessor methods for the iManager structural
columns (id(), active(), created(), updated()).
The class is final readonly. You never construct it yourself;
PageRepository hands one back from every read method.
FQCN + file path
- FQCN:
Scriptor\Boot\Frontend\Page - Source:
boot/Frontend/Page.php
When to use
You touch Page in two places:
- Inside a template as
$site->page(or any page fetched through$site->pages->...). Readslug,template,content, walkparent, etc. - Inside a theme method or action handler that received a page through the repository. Same shape.
You do not subclass Page, do not instantiate it, and do not
mutate it. If you need to write to a page, do it through the
iManager Item lifecycle, not by trying to rewrite the DTO.
Surface
Typed properties (always populated)
| Property | Type | Purpose |
|---|---|---|
item |
Imanager\Domain\Item |
The wrapped iManager item (escape hatch) |
name |
string |
Page name (the iManager name column) |
slug |
string |
URL slug. Empty string identifies the home page (the page that owns the / URL) |
template |
string |
Template name driving template.php for this page |
pagetype |
string |
Page-type identifier (the value the editor's pagetype dropdown sets) |
menu_title |
string |
Short label for navigation, often shorter than name |
content |
string |
The body content (markdown for the default theme path) |
parent |
int |
Parent page id; 0 means top-level |
All seven typed properties fall back to the empty string (or 0
for parent) when the underlying field is missing, so a template
that reads $page->menu_title never raises on a page that has not
filled it in.
Accessor methods (iManager structural columns)
public function id(): ?int
public function active(): bool
public function created(): int
public function updated(): int
These are exposed as methods because they read straight off the
wrapped Item, not off the field bag. id() is nullable because
an unsaved item has no id yet (practically: every page you get
from the repository already has one).
For convenience, the same four are also reachable as properties
through the magic __get():
$page->id // same as $page->id()
$page->active // same as $page->active()
$page->created // same as $page->created()
$page->updated // same as $page->updated()
Magic accessor for custom fields
public function __get(string $name): mixed
public function __isset(string $name): bool
Any property name that is not one of the typed properties or
structural columns falls through to the wrapped item's data bag.
A theme that adds an author field to the Pages category can
read $page->author directly; an unknown name returns null
rather than raising. isset($page->author) is true when the
field exists in the bag.
The escape hatch is $page->item if you ever need to drop down
to the raw iManager Item (custom serialiser, debug dump, etc.).
Lifecycle
Page instances are created by PageRepository whenever it
hands a page back to a caller. One instance per Item, per
read; reads are not deduped. The class is final readonly,
so an instance is frozen at construction time and safe to share
between collaborators in the same request.
The wrapped Item is itself reasonably cheap to read, but
multiple Page constructions for the same id within one request
do hit the Item repository each time. Templates that loop
through hundreds of pages should fetch the list once via
findAll() / findByParent() and iterate the returned array,
not call find($id) in a loop.
Common patterns
Reading the current page in a template
<h1><?= htmlspecialchars($site->page->name, \ENT_QUOTES) ?></h1>
<?php if ($site->page->menu_title !== ''): ?>
<nav aria-label="<?= htmlspecialchars($site->page->menu_title, \ENT_QUOTES) ?>">
<!-- … -->
</nav>
<?php endif ?>
<?php if ($site->page->slug === ''): ?>
<!-- We are on the home page -->
<?php endif ?>
Reading a custom field added through the Editor
$author = $site->page->author ?? 'Editorial'; // __get fall-through
$tags = $site->page->tags ?? []; // unknown keys return null
The ?? 'default' idiom is the canonical guard. __get() already
returns null for unknown names, so ?? does the right thing on
fresh installs where the optional field was never populated.
See also
PageRepository: where everyPageinstance comes fromSite: holds the current page as$site->page- Build a Theme: Page Templates:
walks reading
$site->pagefrom inside a real template