Template helper reference
Helpers are PHP functions available inside any build/ template. They fall into a few families: reading values, rendering editable elements, looping lists, querying the graph, embedding other nodes, and setting up the page. For why helpers are the only bridge to editability, see Template helpers in Build & customize.
Two pairs are easy to confuse: get_val/the_val read a bare value, while get_value/the_value render a full editable element.
Required in every page template
A page template must include three calls or the editor and structured data won’t load:
<?php ctxr_head($data); ?> // in <head> — editor CSS + import map + atoms spec (visitors get PWA head only)
<?php the_schema($data); ?> // in <head> — JSON-LD <script type="application/ld+json">
<?php ctxr_scripts($data); ?> // before </body> — editor bootstrap, or the "Powered by ctxr" bar
the_skip_link() in the <body> is the accessibility convention, and the_hreflang() in the <head> emits locale alternates when a translation connector is active.
Reading values
| Helper | Signature | Does |
|---|---|---|
get_val | get_val(string $name): mixed | Reads a value from context by dot-path; null if it doesn’t resolve |
the_val | the_val(string $name): void | Echoes the value, HTML-escaped |
get_name | get_name(array $data): string | Display name with fallback chain: name → headline → title → @type · @path → @id |
Rendering editable elements
These emit data-inline-* attributes for editors and plain output for visitors.
| Helper | Signature | Does |
|---|---|---|
get_prop / the_prop | (string $name, array $opts = []) | Builds (or echoes) the data-inline-* attribute string; empty for visitors |
get_value / the_value | (string $name, array $opts = []) | Renders <tag>value</tag> with edit attributes |
get_richtext / the_richtext | (string $name, array $opts = []) | Renders a rich-text document model to safe HTML in an editable wrapper |
Options: tag (default span, or div for richtext), class, edit-type (override the auto-detected atom type), item-type, orientation, and fields (for list properties). The atom type is otherwise guessed from the property name.
Lists
| Helper | Signature | Does |
|---|---|---|
get_list | get_list(string $path): array | Pure data: unwraps ListItem wrappers, injects _index/_prefix. Caps at 200 items |
the_list / end_list | the_list(string $path, array $opts = []): array | Opens an editable <ol> and returns the items; close with end_list() |
the_list_item / end_list_item | the_list_item(array $item, array $opts = []): void | Opens an <li> with list-item attributes; close with end_list_item() |
loop | loop(string $path, callable $fn): void | Iterates a list, pushing/popping context per item; callback gets ($item, $index, $prefix) |
Links
Build internal links that stay inside the app shell and respect the current mode. Prefer these over a hand-written <a href>: a raw absolute or cross-space URL breaks the shell (opens outside the installed app) and ignores edit mode.
| Helper | Signature | Does |
|---|---|---|
get_href | get_href(mixed $target = null, array $opts = []): string | Context-aware href. Internal target (a node, an @path, or a path) → root-relative so the shell intercepts it (no full reload); on an actor surface (/edit·/live·/draft) it’s mode-prefixed so links keep you in the same mode; a genuinely external URL (other origin) is returned untouched. null resolves the current context item’s @path |
the_href | the_href(mixed $target = null, string $text = '', array $opts = []): void | Renders <a href="…">text</a> using get_href; external links get target="_blank" rel="noopener". Options: class, title. E.g. the_href(null, 'View', ['class' => 'card-link']) |
Querying the graph
These read the live graph from inside a template (delegating to the query service).
| Helper | Signature | Does |
|---|---|---|
query_by_type | (string $type, ?callable $filter = null): array | All live nodes of a type, optional fn(array): bool filter |
query_by_identifier | (string $propertyID, string $value): array | Nodes found via the external-identifier index |
query_by_appears_in | (string $nodeId, ?string $kind = null): array | Nodes that reference the given node; kind = curatorial / relational / null |
query_more | (string $type, string $parentId, string $cursor = '', int $limit = 25): array | Pages relational items beyond the compiled limit; returns items, numberOfItems, _hasMore, _nextCursor |
query_nodes still exists as a deprecated alias for query_by_type — use the latter.
Embedding other nodes
The get_* variant returns a string; the the_* variant echoes; the short alias equals the the_* variant (except node(), which resolves data).
| Helper | Signature | Does |
|---|---|---|
get_node / node | (string\|array $input): array | Resolves an @id string, id-stub, or array to a data array (no render) |
get_block / the_block / block | (string\|array $data, string $template = ''): string | Renders a blocks/* template. Data first: block($data, 'place-card') |
get_menu / the_menu / menu | (string\|array $data, string $template = ''): string | Renders a menus/* template |
get_page / the_page / page | (array $data, string $template = ''): string | Renders a pages/* template |
get_form / the_form / form | (array $data, string $template = '', array $opts = []): string | Renders a form: explicit template → build/forms/{type}.php → scalar introspection |
A referenced block (one with an @id) gets data-block, data-node-id, data-node-type, data-node-is-live and data-node-is-draft injected on its root element. Template names must match ^[a-zA-Z0-9_\-]+$ and resolve under build/{blocks,menus,pages,forms}/.
Context stack
| Helper | Signature | Does |
|---|---|---|
push_context / pop_context | push_context(array $data): void / pop_context(): void | Push/pop a data frame on the render stack (managed automatically by block() and loop()) |
Page setup and assets
| Helper | Signature | Does |
|---|---|---|
ctxr_head | ctxr_head(array $data = []): void | Editor CSS + import map + atoms spec; visitors get the PWA head only |
ctxr_scripts | ctxr_scripts(array $data = []): void | Editor bootstrap before </body>, or the “Powered by ctxr” bar on branded plans |
the_schema | the_schema(array $data = []): void | Emits the JSON-LD script. get_schema() is a backwards-compatible alias (it echoes, despite the get_ name) |
asset_url | asset_url(string $path): string | Cache-busted theme asset path (?v=mtime); /assets/ maps to build/assets/ |
ctxr_cdn_url | ctxr_cdn_url(string $path): string | Cache-busted platform CDN path |
i18n and accessibility
| Helper | Signature | Does |
|---|---|---|
t | t(string $key, array $args = []): string | Translates a UI string with {var} substitution |
the_skip_link | the_skip_link(string $targetId = 'main'): void | Emits a visible-on-focus skip-to-content link |
the_hreflang | the_hreflang(array $data = []): void | Emits hreflang alternates per locale; no-op without translation alternates |
PWA and render mode
| Helper | Signature | Does |
|---|---|---|
ctxr_pwa_head | ctxr_pwa_head(): void | Manifest link, apple-touch-icon, and the PWA register script |
ctxr_cdn_url | ctxr_cdn_url(string $path): string | Cache-busted platform-CDN path (appends ?v= from the file mtime) |
ctxr_mode | ctxr_mode(): ?string | The render mode from the URL: null (the live product / installable surface), 'edit', 'live', or 'draft' |
You normally don’t call the PWA helpers directly. ctxr_head() already loads the manifest, service-worker registration and the live-page brokers (location, notifications, the permission modal, and the in-page navigation shell) once per page on the live surface — themes just call ctxr_head() and ctxr_scripts() and the PWA comes along. edit_head and edit_scripts are backwards-compatible aliases for ctxr_head and ctxr_scripts; prefer the ctxr_* names.