Why not add a XML sitemap as a small excercise of what a renderer may do? A sitemap basically advertises a search engine direct paths to pages in your website along with a last-modified timestamp at a well known location on your website like this: https://pwflex.team-tofahrn.de/sitemap.xml. The pages contained in this sitemap are managed manually, similar to entries contained in a menu. The sitemap for this site looks like this:
As you can see, there only is a single item type available for this matrix, named "Sitemap Entry". This item plugin is somewhat trivial and basically traverses a page tree, if the checkbox is set or simply outputs that single node only:
<?php namespace ProcessWire;
/*
* Module generating Sitemap entries
*/
class SitemapEntry extends RepeaterFlexItem {
public static function getModuleInfo() {
return array(
'title' => __('Sitemap Entry', __FILE__), // Module Title
'version' => 1,
'renderer' => 'SitemapRenderer',
'head' => '{flex_label} [• {PageSelect}]',
);
}
public static function getFields() {
return([
'@PageSelect' => RepeaterFlexItem::PageSelect([
'columnWidth' => 50,
]),
'@Checkbox' => RepeaterFlexItem::Checkbox([
'columnWidth' => 25,
'label' => __('Enumerate children'),
]),
]);
}
public function renderTree(Page $pg, $depth)
{
$out = $this->ctx->getPageEntry($pg, $depth);
foreach($pg->children as $child)
{
$out .= $this->renderTree($child, $depth+1);
}
return($out);
}
public function render(Page $pg) {
$pgId = $pg->PageSelect;
$pgId = $pg->PageSelect;
if($pg->Checkbox)
$out = $this->renderTree($pgId, 1);
else
$out = $this->ctx->getPageEntry($pgId, 1);
/*
{
foreach($pg->PageSelect->children as $child)
{
$out .= $this->ctx->getPageEntry($child);
}
}
*/
return($out);
}
}
The essential magic happens inside the renderer which not only generates the XML output but may output a human readable undecorated list as well, if requested. So let's take a look at the renderer module:
<?php namespace ProcessWire;
/*
* Simple Sitemap Renderer for RepeaterFlex
*/
class SitemapRenderer extends RepeaterFlexRenderer {
public static function getModuleInfo() { return [
'title' => __('Sitemap Renderer', __FILE__), // Module Title
'summary' => __('Renders RepeaterFlex as a Sitemap.', __FILE__), // Module Summary
'version' => 80,
];
}
protected $depth = 0;
protected $isHuman = false; // Output undecorated list instead of XML
public function setHuman($bHuman) { $this->isHuman = $bHuman; }
// Define prefix and postfix depending on requested output format
public function renderPrefix()
{
$this->depth = 1; // Reset depth
return( $this->isHuman ? '<ul>'
: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">" );
}
public function renderSuffix()
{
return( $this->isHuman ? str_repeat('</ul>', $this->depth)
: "\n</urlset>" );
}
public function getPageEntry(Page $pg, $depth) {
if($pg->isHidden() || $pg->isUnpublished())
return('');
if($this->isHuman)
{
$prefix = '';
$dDepth = $depth - $this->depth;
if($dDepth > 0) $prefix = str_repeat('<ul>', $dDepth);
if($dDepth < 0) $prefix = str_repeat('</ul>', -$dDepth);
$this->depth = $depth;
return($prefix."<li><a href='{$pg->httpUrl}'>{$pg->title}</a></li>");
}
return("
<url>
\t<loc>{$pg->httpUrl}</loc>
\t<lastmod>" . date("Y-m-d", $pg->modified) . "</lastmod>
</url>");
}
}
This renderer does not implement a render method but relies on the default renderer provided by the RepeaterFlex, which invokes renderPrefix first, iterates over all repeater elements and finalizes the output with a call to renderSuffix.
The remarkable point is the render variable $isHuman which controls the output format and may be controlled from the template code, so it's worth to take a look at that as well:
<?php namespace ProcessWire;
$sm = $page->flex_sitemap; // Reference the sitemap definition
if(isset($input->human)) // Human output requested?
{ // Output content markup, this site uses delayed output from $content variable
$content = "<div class='uk-container uk-padding'><h1>{$page->title}</h1>";
$sm->getContext()->setHuman(true); // Request regular HTML markup
$content .= $sm->render(); // Get contents
$content .= '</div>';
}
else
{ // Regular XML output
header("Content-Type: text/xml"); // Switch reply header declare correct format
echo $sm->render(); // Get contents as XML
die(); // Stop any processing NOW
}
The template checks for a specified url parameter "human" and then renders the sitemap as a simple, undecorated list: https://pwflex.team-tofahrn.de/sitemap.xml?human