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

When to use

You touch Page in two places:

  • Inside a template as $site->page (or any page fetched through $site->pages->...). Read slug, template, content, walk parent, 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