PagePathHistory
Tracks historical URLs for pages and automatically performs 301 (permanent) redirects when a past URL is requested
Whenever a page is moved or renamed, Page Page Expand all Collapse all API reference Returns an array of all paths a page has previously had, ordered oldest to newest. Options array: When Returns detailed information about a path if it exists in the history. Returns an
associative array with the following keys: Only pages with templates that allow URL segments are considered for partial matches. Given a historical path, returns the This method traverses path segments recursively (up to Returns an array of all root-level segments (first path component after Returns Records a historical path for a page and removes any existing entries for the page's
current path (since those are no longer applicable). Returns This is the preferred method for recording history. It calls Adds a historical path without cleaning up current-path entries. Returns Note: The path cannot collide with an existing page's current URL. If a live page
already occupies that path, the method returns Deletes a single historical path entry for the given page. Returns the number of
rows deleted (0 or 1). Deletes all historical paths. Pass a Page After installation, The module provides one configurable setting via the admin UI: Minimum age (seconds) — Pages must exist for at least this many seconds before
their previous paths are recorded. Default is 120 seconds (2 minutes). This prevents
recording history for pages that are immediately moved or renamed during initial setup. The module configuration screen also allows deleting all recorded historical paths. to the new location whenever the past URL is accessed. Click any linked item for full usage details and examples. Hookable methods are indicated with the icon. In addition to those shown below, the In addition to the methods and properties above, Page// The module is autoloaded and available via $modules:
$pathHistory = $modules->get('PagePages::moved, Pages::renamed, and ProcessPageView::pageNotFound
automatically. Most of the time you don't need to call it directly — it works silently in the
background. Use the API methods when you need to query history, manually record paths, or
integrate custom redirect logic.Property Type Default Description minimumAgeint120Minimum age in seconds a page must have before its previous path is recorded. rootSegmentsarray|boolfalseCached list of all root-level path segments ever recorded. Automatically rebuilt. Constant Value Description dbTableName'page_path_history'Name of the database table used by this module minimumAge120Default minimum age (seconds) before tracking a page's path maxSegments10Maximum path segments for recursive lookups getPathHistory($page, $options = [])
// Get all historical paths for a page as strings
$paths = $pathHistory->getPathHistory($page);
// ['/old-blog/my-post/', '/blog/old-slug/', '/blog/my-post/']
// Limit to a specific language
$paths = $pathHistory->getPathHistory($page, $languages->get('de'));
// Get verbose info (dates, languages)
$paths = $pathHistory->getPathHistory($page, ['verbose' => true]);
foreach($paths as $p) {
echo $p['path'] . ' — ' . $p['date'] . "\n";
}
// Shortcut: boolean true enables verbose mode
$paths = $pathHistory->getPathHistory($page, true);Option Type Default Description languageLanguage|int|stringnullLimit results to this language. null = all languages.verboseboolfalseReturn associative array with path, date, and language keys.virtualbooltrueInclude auto-determined virtual entries from parent history. virtual is true (default), the method also includes paths resulting from parent
page moves — URLs that would have been valid for the page even though the page itself
didn't move. This gives a complete picture of all URLs that once led to the page.getPathInfo($path, array $options = [])
Key Type Description idintID of the matched page, or 0 if no matchpathstringThe matched path language_idintLanguage ID, if applicable templates_idintTemplate ID of the matched page parent_idintParent ID of the matched page statusintStatus flags of the matched page createdstringISO-8601 date string when the entry was created namestringPage name (in default language) matchTypestring'exact' for exact match, 'partial' for URL segments match, or '' for no matchurlSegmentStrstringURL segments portion of the path (only for partial matches) $info = $pathHistory->getPathInfo('/old-url/');
if($info['id']) {
echo "Found page ID {$info['id']} at {$info['path']}\n";
}
// Allow partial matches with URL segments
$info = $pathHistory->getPathInfo('/old-url/segment1/segment2/', [
'allowUrlSegments' => true
]);
if($info['matchType'] === 'partial') {
echo "URL segments: {$info['urlSegmentStr']}\n";
}getPage($path, $level = 0)
Page object that once lived there, or a NullPage
if no match is found. If the path is language-specific, the returned page will have a
_language property set to the corresponding Language object.$page = $pathHistory->getPage('/old-blog/my-post/');
if($page->id) {
// Check if this redirect is for a specific language
$language = $page->get('_language');
if($language) {
echo "Redirecting to {$language->title} version: {$page->url}\n";
} else {
echo "Redirecting to: {$page->url}\n";
}
}maxSegments levels) to find
matches even when parent paths have changed multiple times.getRootSegments()
/) that have
ever appeared in the path history. Useful for efficient preliminary checks before
performing deeper lookups.$segments = $pathHistory->getRootSegments();
// ['blog', 'about', 'products', 'old-site', …]isRootSegment($segment)
true if the given segment has ever existed as a root-level path component
in the history. Accepts either a single segment or a full path (the root segment is
extracted automatically).if($pathHistory->isRootSegment('blog')) {
// 'blog' has existed at the root level before
}
// Also accepts full paths:
if($pathHistory->isRootSegment('/old-site/some/page')) {
// '/old-site/' is a known root segment
}setPathHistory(Page $page, $path, $language = null)
true on success,
or false if the path is already consumed in history.// Record a path for the default language
$pathHistory->setPathHistory($page, '/old-path/');
// Record a language-specific path
$pathHistory->setPathHistory($page, '/de/alter-pfad/', $languages->get('de'));addPathHistory() internally
and then cleans up overlapping current-path entries.addPathHistory(Page $page, $path, $language = null)
true if the
path was added, or false if it overlaps with an existing live path (checked via
PagePaths when available).$pathHistory->addPathHistory($page, '/some-historical-path/');false and does not record the entry.deletePathHistory(Page $page, $path)
$deleted = $pathHistory->deletePathHistory($page, '/old-url/');
if($deleted) echo "Path removed from history\n";deleteAllPathHistory($page)
Page object to delete history for one page,
or true to delete all history for all pages.// Delete all history for one page
$pathHistory->deleteAllPathHistory($page);
// Delete all history for every page
$pathHistory->deleteAllPathHistory(true);Hooks added by the module
Hook When Pages::movedAfter a page is moved to a different parent Pages::renamedAfter a page is renamed Pages::deletedAfter a page is deleted (cleans up history) ProcessPageView::pageNotFoundWhen a 404 occurs (performs automatic redirect) Page::addUrl($url, $language)When adding a custom URL via $page->addUrl()Page::removeUrl($url, $language)When removing a custom URL via $page->removeUrl()$page->addUrl() and $page->removeUrl() become available
as methods on every Page object:// Add a custom historical URL for the page
$page->addUrl('/custom-redirect/', $languages->get('de'));
// Remove a previously added URL
$page->removeUrl('/custom-redirect/');Hookable module methods
Hook When Arguments PageModule is installed — PageModule is uninstalled — PageModule is upgraded $fromVersion, $toVersion// Hook after install to seed initial history
$wire->addHookAfter('Page// Set via API
$pathHistory = $modules->get('Page301 Moved Permanently redirect to the
page's current URL, including the correct language when applicable./blog/5134.3096.83_page-name) are excluded.getPathInfo() can match paths with additional URL segments,
but only when the matched page's template has URL segments enabled.$page->addUrl() and $page->removeUrl() are added to every Page object
by this module, enabling programmatic URL history management.wire/modules/Page/PageAPI reference: methods, properties, hooks
Page class also inherits all the methods and properties of: WireData and Wire.Common
Properties
Name Return Summary Page int Page array bool Additional methods and properties
API reference based on ProcessWire core version 3.0.269