ModelPages module

Container

A page tree node that owns a URL segment but never renders a page of its own.
A container exists so that a path such as /solutions/for-government can carry a /solutions segment without /solutions itself being a destination.
It carries a slug, a name (through one minimal revision) and a publish state, and it participates in the page tree - but it has no layout, no areas and no address of its own.
Requesting the container's own URL either returns a 404 or redirects to a nominated page; see {@link $behavior}.

A model in the Pages module, with 70 members for templates, 52 API properties and 72 members for admin templates.

Pages\Models\Frontend\Container Pages\Models\Api\Container Pages\Models\Backend\Container Also in Pages: Page PageArea PageRevision
View
What Smarty and Twig templates receive. Properties in REST API responses and job payloads. What admin screen templates receive when you override them.

Templates view

Properties 47

Take no arguments.

# __toString() Templates only
string
Renders the container as its display name.
A container has no layout and no address, so there is nothing to render but the label a theme would show for it - see {@link \Pages\Models\Container::getOriginalFullPath()}.
# isContainer()
bool
# getUrl()
string
A container has no URL, because it has no address. The page implementation prefixes the effective path with the site's primary hostname, which for an empty path is a working link to the homepage. The Frontend and Api response models expose this value to themes, headless clients and GraphQL, so without this override a navigation built from it would link the container to the homepage.
# getOriginalFullPath()
string
A container owns a URL segment but has no address of its own, so it never resolves to a path. The empty string is the framework-wide signal for "this record has no URL": the sitemap generator skips it while still descending into its children, and the framework's own link builders - getFullPath(), getUrl(), the show-frontend action link - return nothing rather than a wrong link. How an empty URL renders is up to the theme, which should branch on isContainer() to show a container as a label rather than a link. That holds even when the container is configured to redirect - the redirect is a courtesy for typed, bookmarked or legacy hits, not an address we publish.
# getFullPath()
string
A container's effective path is empty even when a custom URL is set on it. The container form offers no custom URL, but the API can still attach one, and the default getFullPath() returns a custom URL ahead of the original path - which would put the container back into the sitemap, menus and every link built from the effective path. Pages beneath the container are unaffected: their paths are built from each ancestor's slug, never from the ancestor's path.
# getBehavior()
?string
The behavior applied when the container's own URL is requested
# getRedirectCode()
?int
The HTTP status code used when the container redirects
# getRedirectPage()
?Page
The page the container redirects to when its own URL is requested
# getParent()
?Page
Get the parent for the page
# getSourcePage()
?Page
The source page this page was translated from
# getRelatedPages()
?Collection<Page>
The pages this page has been translated to
# getName()
?string
Get the title of the page
# getAssignedWorkflow()
?Workflow
Get the workflow assigned to the record or entity
# hasAssignedWorkflow()
bool
Check whether the record has an assigned workflow
# getBranches()
Collection
Get the branches associated with the record
# getNumRevisionsWithoutBranch()
int
Get the number of revisions without branch
# getOriginalPath()
string
It's an alias and deprecated! Directly use getOriginalFullPath() instead.
# getOriginalFullSlug()
string
Gets the record's own canonical slug, built from the record's data and _ignoring_ any custom URL override. Like getFullSlug() it excludes the module path, has no leading slash, and includes ancestor slugs for hierarchical records — but it always reflects the record's real slugs rather than a custom URL. For a non-hierarchical record this equals the record's own getSlug() value. This default concatenates whatever the ancestor walk supplies and does not judge the segments: an ancestor with no slug of its own contributes an empty segment rather than failing. A model whose callers need to tell an unresolvable chain apart from a valid one should either return an empty string from an override, as Products\Models\ProductDescription does, or expose a separate check alongside it, as Pages\Models\BasePage::hasResolvableSlugChain() does.
# getFullSlug()
string
Gets the slug that addresses this record relative to its module — the portion of the URL that identifies the record itself, without the module path and without a leading slash. For a hierarchical record the slug includes the slugs of all of its ancestors joined with slashes. This is the effective slug: when a custom URL is set on the record, it _takes precedence and is returned_ here. The no-leading-slash shape describes the canonical value only. A custom URL is returned verbatim, and custom URLs are stored with a leading slash, so a record that has one returns /my-custom rather than my-custom; use getOriginalFullSlug() when you need the canonical shape unconditionally.
# getId()
?int
The ID of the page
# getSlug()
?string
The slug for the page, i.e. the url portion identifying the page. Does not include parent slugs or the module slug.
# getPublishAt()
?DateTime
Date at which the page will be published or has been published
# getUnpublishAt()
?DateTime
Date after which the page should not be shown anymore
# getPosition()
?int
Position of the page in the list of pages at the same level (i.e. with the same parent)
# getCreated()
?DateTime
Date at which the page was created
# getModified()
?DateTime
Date at which the page was last modified
# getPublished()
?DateTime
Date at which the page was published
# getSite()
?Site
Site associated with the page
# getActiveRevision()
?PageRevision
The active revision for the page
# getChildren()
?Collection<Page>
Child pages, i.e. pages located underneath this page
# getRevisions()
?Collection<PageRevision>
Revisions for the page
# getLastRevision()
?PageRevision
Get the last revision for the entity. If a revision has been added, this will be the newly added revision.
# getTags()
?Collection<Tag>
Tags associated with the page
# getCategories()
?Collection<Category>
Categories associated with the page
# isPublished()
bool
Determines if the record is published.
# getOwner()
?User
The owner of the page, possibly having different access rights to the page.
# getLanguage()
?Language
The language for the page
# getSeoSettings()
?SeoSettings
The SEO settings for the page, like title, descriptions, robots and keywords
# getCustomUrl()
?CustomUrl
Get the custom url used for the page. This will take precedence over the slugs of this page, parent page and the module.
# getFullPosition()
string
Get the full, dot-separated full position for hierarchical records
# getExternalId()
?string
# isNewRecord() Not in API & Jobs
bool
Check if the record is a new record
# isHierarchicalModel() Not in API & Jobs
bool
Check whether a model is a hierarchical model, i.e. can have children
# isVersionedModel() Not in API & Jobs
bool
Check whether a model is versioned
# getOtherLanguageVersions() Templates only
array
Fetch an associative array with other language versions of this page The key of the array is the language code id (e.g. "en-us") and the value is the page.
# getHtml() Templates only
string
Gets the rendered HTML for the record using its layout template.
# getAllParents() Templates only
Collection
Get all parents for the current record, ordered by root record => the closest record

Methods 23

Take arguments. The API serialises only getters that need none.

# getBranchRevisions(RevisionBranch $branch, ?int $limit, ?int $offset)
Collection
Get the revisions for the provided branch, ordered by id ascending. Pass a limit/offset to fetch a bounded window instead of the full set — loading all revisions of a large branch exhausts memory.
# getNumBranchRevisions(RevisionBranch $branch)
int
Get the number of revisions for the provided branch (COUNT query — does not hydrate)
# getRevisionsWithoutBranch(?int $limit, ?int $offset)
Collection
Get the revisions without branch, ordered by id ascending. Pass a limit/offset to fetch a bounded window instead of the full set — loading all revisions of a record with a large history exhausts memory.
# getRevisionById(int $id)
?PageRevision
Get the revision with the specified id
# getAreaHtml(string $areaName) Templates only
?string
Get the html for the area with the specified name
# hasAreaWithName(string $areaName) Templates only
bool
Check if an area with the specified name exists for the active revision of the record
# getAreaType(string $areaName) Templates only
?InputType
Get the input type for the specified layout area, using the active revision of the record. Returns null if the area with the specified name could not be found.
# getAreaCategory(string $areaName) Templates only
?string
Get the category for the specified layout area, using the active revision of the record. Returns null if the area with the specified name could not be found.
# getAreaEditor(string $areaName) Templates only
?string
Get the editor for the specified layout area, using the active revision of the record. Returns null if the area with the specified name could not be found.
# getAreaImage(string $areaName) Templates only
?Image
Get the image for the layout area with the specified name, using the active revision of the record
# getAreaImageOrPlaceholder(string $areaName, int $width, int $height) Templates only
Image
Get the image for the component area with the specified name. Uses the active revision of the global component. If the image does not exist, get a placeholder image with the specified dimensions Will always return an image, even if it was not found (in that case, an empty image).
# getAreaFile(string $areaName) Templates only
?File
Get the file for the layout area with the specified name, using the active revision of the record
# getAreaRecord(string $areaName) Templates only
?Model
Get the record for the layout area with the specified name, using the active revision of the record
# getAreaRecords(string $areaName) Templates only
?Collection
Get the records for the layout area with the specified name, using the active revision of the record
# getAreaText(string $areaName) Templates only
?string
Get the text for the layout area with the specified name, using the active revision of the record
# getAreaValues(string $areaName) Templates only
array
Get the values for the layout area with the specified name, using the active revision of the record
# getAreaDate(string $areaName) Templates only
?DateTime
Get a date for the layout area with the specified name, using the active revision of the record
# getAreaWidget(string $areaName) Templates only
?Widget
Get the widget for the layout area with the specified name, using the active revision of the record
# getAreaComponent(string $areaName) Templates only
?Component
Get the component for the layout area with the specified name, using the active revision of the record
# getAreaCollectionElements(string $areaName) Templates only
?Collection
Get the component collection elements for the layout area with the specified name, using the active revision of the record
# getAreaDefaultContent(string $areaName) Templates only
?Content
Get the default content of the layout area. Uses the active revision of the global component. Will return a BlankContent if no content was defined.
# getAreaContentOrDefault(string $areaName) Templates only
?Content
Get the content of the layout area, or the default content if no content exists. Uses the active revision of the global component. If no default content exists, a BlankContent will be returned.
# isAreaBlank(string $areaName) Templates only
bool
Check whether the layout area with the specified name has no usable content, using the active revision of the record. Reports the area as blank when the record has no active revision. Blank describes what is stored, not what renders: the answer comes from the stored content and never renders a template. An area can therefore be not blank and still render nothing — a record that was deleted or has no layout, a component or widget whose template outputs nothing, or text whose only content is a placeholder for a deleted image. To decide on the rendered output instead, test the result of getAreaHtml().

API & Jobs view

API & JobsEach property is a key in REST API responses and job payloads. Its name is the template getter without get, is or has. Methods that need arguments are not available here.

Properties 52

Read-only values on the record.

# container
bool
# url
string
A container has no URL, because it has no address. The page implementation prefixes the effective path with the site's primary hostname, which for an empty path is a working link to the homepage. The Frontend and Api response models expose this value to themes, headless clients and GraphQL, so without this override a navigation built from it would link the container to the homepage.
# duplicateUrl Not in Templates
?string
Containers are not duplicable - a copy would carry a nominated redirect target that belongs to the original
# originalFullPath
string
A container owns a URL segment but has no address of its own, so it never resolves to a path. The empty string is the framework-wide signal for "this record has no URL": the sitemap generator skips it while still descending into its children, and the framework's own link builders - getFullPath(), getUrl(), the show-frontend action link - return nothing rather than a wrong link. How an empty URL renders is up to the theme, which should branch on isContainer() to show a container as a label rather than a link. That holds even when the container is configured to redirect - the redirect is a courtesy for typed, bookmarked or legacy hits, not an address we publish.
# fullPath
string
A container's effective path is empty even when a custom URL is set on it. The container form offers no custom URL, but the API can still attach one, and the default getFullPath() returns a custom URL ahead of the original path - which would put the container back into the sitemap, menus and every link built from the effective path. Pages beneath the container are unaffected: their paths are built from each ancestor's slug, never from the ancestor's path.
# behavior
?string
The behavior applied when the container's own URL is requested
# redirectCode
?int
The HTTP status code used when the container redirects
# redirectPage
?Page
The page the container redirects to when its own URL is requested
# parent
?Page
Get the parent for the page
# createChildUrl Not in Templates
?string
Get the url for creating a child record.
# editUrl Not in Templates
?string
Get the url for editing the record.
# moveUrl Not in Templates
?string
Get the url for moving the record.
# deleteUrl Not in Templates
?string
Get the url for deleting the record.
# publishUrl Not in Templates
?string
Get the url for publishing the record.
# manageUrl Not in Templates
?string
Get the url for managing records.
# permissionsUrl Not in Templates
?string
Get the url for editing the permissions of the record.
# checkPermissionsUrl Not in Templates
?string
Get the url for checking user permissions for this record.
# showFrontendUrl Not in Templates
?string
Get the url showing the record in the frontend
# sourcePage
?Page
The source page this page was translated from
# relatedPages
?Collection<Page>
The pages this page has been translated to
# name
?string
Get the name for the active revision
# assignedWorkflow
?Workflow
Get the workflow assigned to the record or entity
# assignedWorkflow
bool
Check whether the record has an assigned workflow
# branches
Collection
Get the branches associated with the record
# numRevisionsWithoutBranch
int
Get the number of revisions without branch
# originalPath
string
It's an alias and deprecated! Directly use getOriginalFullPath() instead.
# originalFullSlug
string
Gets the record's own canonical slug, built from the record's data and _ignoring_ any custom URL override. Like getFullSlug() it excludes the module path, has no leading slash, and includes ancestor slugs for hierarchical records — but it always reflects the record's real slugs rather than a custom URL. For a non-hierarchical record this equals the record's own getSlug() value. This default concatenates whatever the ancestor walk supplies and does not judge the segments: an ancestor with no slug of its own contributes an empty segment rather than failing. A model whose callers need to tell an unresolvable chain apart from a valid one should either return an empty string from an override, as Products\Models\ProductDescription does, or expose a separate check alongside it, as Pages\Models\BasePage::hasResolvableSlugChain() does.
# fullSlug
string
Gets the slug that addresses this record relative to its module — the portion of the URL that identifies the record itself, without the module path and without a leading slash. For a hierarchical record the slug includes the slugs of all of its ancestors joined with slashes. This is the effective slug: when a custom URL is set on the record, it _takes precedence and is returned_ here. The no-leading-slash shape describes the canonical value only. A custom URL is returned verbatim, and custom URLs are stored with a leading slash, so a record that has one returns /my-custom rather than my-custom; use getOriginalFullSlug() when you need the canonical shape unconditionally.
# id
?int
The ID of the page
# slug
?string
The slug for the page, i.e. the url portion identifying the page. Does not include parent slugs or the module slug.
# publishAt
?DateTime
Date at which the page will be published or has been published
# unpublishAt
?DateTime
Date after which the page should not be shown anymore
# position
?int
Position of the page in the list of pages at the same level (i.e. with the same parent)
# created
?DateTime
Date at which the page was created
# modified
?DateTime
Date at which the page was last modified
# published
?DateTime
Date at which the page was published
# site
?Site
Site associated with the page
# activeRevision
?PageRevision
The active revision for the page
# children
?Collection<Page>
Child pages, i.e. pages located underneath this page
# revisions
?Collection<PageRevision>
Revisions for the page
# lastRevision
?PageRevision
Get the last revision for the entity. If a revision has been added, this will be the newly added revision.
# tags
?Collection<Tag>
Tags associated with the page
# categories
?Collection<Category>
Categories associated with the page
# published
bool
Determines if the record is published.
# owner
?User
The owner of the page, possibly having different access rights to the page.
# language
?Language
The language for the page
# seoSettings
?SeoSettings
The SEO settings for the page, like title, descriptions, robots and keywords
# customUrl
?CustomUrl
Get the custom url used for the page. This will take precedence over the slugs of this page, parent page and the module.
# fullPosition
string
Get the full, dot-separated full position for hierarchical records
# externalId
?string

Admin view

AdminAvailable in cdna-admin templates. Copy an admin template into your site repository to change how this record is edited.

Properties 65

Take no arguments.

# isContainer()
bool
# getUrl()
string
A container has no URL, because it has no address. The page implementation prefixes the effective path with the site's primary hostname, which for an empty path is a working link to the homepage. The Frontend and Api response models expose this value to themes, headless clients and GraphQL, so without this override a navigation built from it would link the container to the homepage.
# getDuplicateUrl() Not in Templates
?string
Containers are not duplicable - a copy would carry a nominated redirect target that belongs to the original
# getOriginalFullPath()
string
A container owns a URL segment but has no address of its own, so it never resolves to a path. The empty string is the framework-wide signal for "this record has no URL": the sitemap generator skips it while still descending into its children, and the framework's own link builders - getFullPath(), getUrl(), the show-frontend action link - return nothing rather than a wrong link. How an empty URL renders is up to the theme, which should branch on isContainer() to show a container as a label rather than a link. That holds even when the container is configured to redirect - the redirect is a courtesy for typed, bookmarked or legacy hits, not an address we publish.
# getFullPath()
string
A container's effective path is empty even when a custom URL is set on it. The container form offers no custom URL, but the API can still attach one, and the default getFullPath() returns a custom URL ahead of the original path - which would put the container back into the sitemap, menus and every link built from the effective path. Pages beneath the container are unaffected: their paths are built from each ancestor's slug, never from the ancestor's path.
# getBehavior()
?string
The behavior applied when the container's own URL is requested
# getRedirectCode()
?int
The HTTP status code used when the container redirects
# getRedirectPage()
?Page
The page the container redirects to when its own URL is requested
# getParent()
?Page
Get the parent for the page
# getImageRootTargetFolderPath() Admin only
string
Retrieves the root target folder path for uploaded images.
# getCreateChildUrl() Not in Templates
?string
Get the url for creating a child record.
# getEditUrl() Not in Templates
?string
Get the url for editing the record.
# getMoveUrl() Not in Templates
?string
Get the url for moving the record.
# getDeleteUrl() Not in Templates
?string
Get the url for deleting the record.
# getPublishUrl() Not in Templates
?string
Get the url for publishing the record.
# getManageUrl() Not in Templates
?string
Get the url for managing records.
# getPermissionsUrl() Not in Templates
?string
Get the url for editing the permissions of the record.
# getCheckPermissionsUrl() Not in Templates
?string
Get the url for checking user permissions for this record.
# getShowFrontendUrl() Not in Templates
?string
A container is never a destination, so the page tree offers no "view in frontend" action for it. The inherited implementation already returns null for an empty path, but stating it here documents the intent rather than leaving it to a side-effect.
# getSourcePage()
?Page
The source page this page was translated from
# getRelatedPages()
?Collection<Page>
The pages this page has been translated to
# getName()
?string
Get the name for the active revision
# getImageTargetFolderPath() Admin only
string
Retrieves the target folder path for uploaded images. Images will be moved to this path after the upload has been processed.
# isSupportingBranches() Admin only
bool
Checks whether the record supports branches
# getAssignedWorkflow()
?Workflow
Get the workflow assigned to the record or entity
# hasAssignedWorkflow()
bool
Check whether the record has an assigned workflow
# getBranches()
Collection
Get the branches associated with the record
# getNumRevisionsWithoutBranch()
int
Get the number of revisions without branch
# getOriginalPath()
string
It's an alias and deprecated! Directly use getOriginalFullPath() instead.
# getOriginalFullSlug()
string
Gets the record's own canonical slug, built from the record's data and _ignoring_ any custom URL override. Like getFullSlug() it excludes the module path, has no leading slash, and includes ancestor slugs for hierarchical records — but it always reflects the record's real slugs rather than a custom URL. For a non-hierarchical record this equals the record's own getSlug() value. This default concatenates whatever the ancestor walk supplies and does not judge the segments: an ancestor with no slug of its own contributes an empty segment rather than failing. A model whose callers need to tell an unresolvable chain apart from a valid one should either return an empty string from an override, as Products\Models\ProductDescription does, or expose a separate check alongside it, as Pages\Models\BasePage::hasResolvableSlugChain() does.
# getFullSlug()
string
Gets the slug that addresses this record relative to its module — the portion of the URL that identifies the record itself, without the module path and without a leading slash. For a hierarchical record the slug includes the slugs of all of its ancestors joined with slashes. This is the effective slug: when a custom URL is set on the record, it _takes precedence and is returned_ here. The no-leading-slash shape describes the canonical value only. A custom URL is returned verbatim, and custom URLs are stored with a leading slash, so a record that has one returns /my-custom rather than my-custom; use getOriginalFullSlug() when you need the canonical shape unconditionally.
# getId()
?int
The ID of the page
# getSlug()
?string
The slug for the page, i.e. the url portion identifying the page. Does not include parent slugs or the module slug.
# getPublishAt()
?DateTime
Date at which the page will be published or has been published
# getUnpublishAt()
?DateTime
Date after which the page should not be shown anymore
# getPosition()
?int
Position of the page in the list of pages at the same level (i.e. with the same parent)
# getCreated()
?DateTime
Date at which the page was created
# getModified()
?DateTime
Date at which the page was last modified
# getPublished()
?DateTime
Date at which the page was published
# getSite()
?Site
Site associated with the page
# getActiveRevision()
?PageRevision
The active revision for the page
# getChildren()
?Collection<Page>
Child pages, i.e. pages located underneath this page
# getRevisions()
?Collection<PageRevision>
Revisions for the page
# getLastRevision()
?PageRevision
Get the last revision for the entity. If a revision has been added, this will be the newly added revision.
# getTags()
?Collection<Tag>
Tags associated with the page
# getCategories()
?Collection<Category>
Categories associated with the page
# isPublished()
bool
Determines if the record is published.
# getOwner()
?User
The owner of the page, possibly having different access rights to the page.
# getLanguage()
?Language
The language for the page
# getSeoSettings()
?SeoSettings
The SEO settings for the page, like title, descriptions, robots and keywords
# getCustomUrl()
?CustomUrl
Get the custom url used for the page. This will take precedence over the slugs of this page, parent page and the module.
# getFullPosition()
string
Get the full, dot-separated full position for hierarchical records
# getExternalId()
?string
# isNewRecord() Not in API & Jobs
bool
Check if the record is a new record
# isHierarchicalModel() Not in API & Jobs
bool
Check whether a model is a hierarchical model, i.e. can have children
# isVersionedModel() Not in API & Jobs
bool
Check whether a model is versioned
# getAvailableSites() Admin only
Collection
Fetches the available sites. Returns all sites that are not already assigned to a page, unless it is the current page
# getAvailableLayouts() Admin only
Collection
Get the available layouts for showing pages
# getAvailableUsers() Admin only
Collection
Get the available users
# getContainerIds() Admin only
array
Ids of every container, for use as a tree-select picker's disableOptions. Containers share the pages table through single-table inheritance, so a picker built on Pages\Entities\Page lists them and has no way to tell them apart. Disabling them keeps the tree shape - descendants of a container stay reachable - while blocking selection where a container is not a valid answer, such as a translation source or a link target.
# getDisplayProperty() Admin only
string
Get the display property to use
# getDisplaySlug() Admin only
string
Get the slug to display in the backend. Returns the site's primary hostname for homepages.
# getAvailableSaveNewActions() Admin only
array
Get the actions available for saving a new version of the record. Determined whether an active revision can be set directly, and in which order the actions should be returned.

Methods 7

Take arguments. The API serialises only getters that need none.

# getBranchRevisions(RevisionBranch $branch, ?int $limit, ?int $offset)
Collection
Get the revisions for the provided branch, ordered by id ascending. Pass a limit/offset to fetch a bounded window instead of the full set — loading all revisions of a large branch exhausts memory.
# getNumBranchRevisions(RevisionBranch $branch)
int
Get the number of revisions for the provided branch (COUNT query — does not hydrate)
# getRevisionsWithoutBranch(?int $limit, ?int $offset)
Collection
Get the revisions without branch, ordered by id ascending. Pass a limit/offset to fetch a bounded window instead of the full set — loading all revisions of a record with a large history exhausts memory.
# getRevisionById(int $id)
?PageRevision
Get the revision with the specified id
# getAvailableSaveActions(?Model $currentRevision) Admin only
array
Get the save actions available for the record and provided revision being edited
# getRevisionActions(Revision $revision, ?Revision $currentRevision) Admin only
array
Get the actions available for the provided revision
# getUpdateRevisionId(Revision $revision) Admin only
?int
Get the revision ID to use when updating the record. If the provided revision is a draft, the previous id is returned, if one exists.