Dieses Kapitel widmet sich den APF-Tags (a.k.a. Taglibs), deren Bedeutung und Implementierung.
Der Page-Controller basiert auf dem gleichnamigen Software-Entwurfsmuster und ist ein zentraler Bestandteil des APF. Er ermöglicht dem Entwickler über das Tag-Konzept, eigene Funktionalitäten zur Erzeugung und Transformation des HMVC-DOM-Baumes zu injizieren. Tags bilden damit nicht nur die Basis des APF-HMVC-Konzepts, sondern können auch zur Erweiterung desselben genutzt werden.
Des Weiteren können Tags dazu verwendet werden um darin enthaltene Funktionalitäten in unterschiedlichen Projekten wiederzuverwenden. APF-Templates verarbeiten - als Schutz vor unkontrollierter Verteilung von Logik - lediglich Tags und keinen PHP-Code. Damit ist automatisch sichergestellt, dass View-Logik lediglich in Tags oder Controller enthalten sein kann.
Aufgabe des Page-Controller ist der Aufbau und die Transformation der internen Template-Struktur. Hierzu bietet er ein Tag-API, die einem definierten Timing-Modell folgend Tags einliest, daraus Dokumente im Baum erzeugt und aus dem Baum anschließend eine HTML-Ausgabe erzeugt. Andere Formate wie beispielsweise XML sind ebenfalls möglich. Der Parser ist kein "echter" XML-Parser, beherrscht jedoch die Auflösung von explizit- und selbst-schließenden Tags sowie die Schachtelung von Tags in beliebig tiefen Hierarchien.
Die mit dem APF ausgelieferten Standard TagLibs bieten bereits eine Reihe an Standard-Funktionalitäten wie das Einbinden einer weiteren Template-Datei, die Definition von wiederverwendbaren Template-Fragmenten und das Bekanntgeben von eigenen Tags an.
Der XMLParser dient dem Page-Controller zur Analyse einer Tag-Definition im Template und wird bei der Erstellung von Document-Instanzen genutzt.
Ein APF-Tag definiert sich durch folgende Bestandteile:
Folgende Tag-Definition könnte zur Ausgabe des aktuellen Datums in einem Template genutzt werden:
<current:date format="H:i" />
Dabei ist current das Prefix des Tags, date der Name und das Attribut format beinhaltet das Ausgabe-Format. Der Tag definiert keinen Inhalt.
Zur Darstellung einer Bildergalerie könnte folgendes Tag genutzt werden:
<img:gallery>
<h3>Mein Urlaub 2012</h3>
<p>
Diese Bilder stammen aus meinem Urlaub im Jahr 2012:
</p>
<gallery:datasource
namspace="..."
class="..."
/>
</img:gallery>
In diesem Beispiel beinhaltet der <img:gallery />-Tag statisches HTML, was später zur Formatierung der Ausgabe genutzt werden kann und einen weiteren Tag zur Angabe der Datenquelle (z.B. aus einer Datenbank). Zur Verwendung des Tags muss auch dieses zunächst dem APF-Parser bekannt gegeben werden.
Wie im Kapitel Kapitel 2 angesprochen erzeugt der Page-Controller aus der Tag-Struktur in den Template-Dateien einen DOM-Baum. Dieser ähnelt dem eines Browsers, der aus den HTML-Tags ebenfalls eine Speicherstruktur erzeugt.
Der Unterschied zur DOM-Struktur eines Browsers besteht darin, dass die einzelnen Knoten jeweils eigene Funktionalität zur Transformation bzw. Darstellung beinhalten, die der Page-Controller während der Transformationsphase nutzt. Details zum Timing-Modell des Page-Controller finden Sie hier.
Jeder Tag - bzw. ab einem definierten Zeitpunkt seine Instanz - durchläuft einen definierten Zyklus. Zunächst wird die Struktur des Tags analysiert, dann seine Substanz (Attribute und Inhalt). Anschließend wird gemäß der Tag-Definition eine Instanz des Tags erstellt und mit dem vom XMLParser analysierten Inhalt initialisiert.
Der Page-Controller verarbeitet alle Tags der Form
<prefix:name />
(selbstschließend) bzw.
<prefix:name></prefix:name>
(explizit schließend), die in einer Template-Datei definiert sind. Gemäß Kapitel 2 können Tags eine beliebige Anzahl von Attributen beinhalten.
Bei der Analyse eines Templates greift der APF-Parser auf die Liste der registrierten Tags zurück um die Tag-Instanzen zu erstellen. Diese Liste beinhaltet den Klassen-Namen, der die Implementierung eines Tags beschreiben. Die mit dem APF mitgelieferten Tags werden bereits in der bootstrap.php registriert.
Tags lassen sich sowohl statisch für alle Templates als auch dynamisch pro DOM-Knoten registrieren. Dies ermöglicht es, die für ein Projekt eingesetzten Tags an einer zentralen Stelle zu registrieren und gleichzeitig - sofern notwendig - Standard-Tags des APF lokal überschreiben.
Möchten Sie einen Tag für das gesamte Projekt registrieren, lässt sich das sehr einfach in der Bootstrap-Datei erledigen:
Document::addTagLib('VENDOR\..\DateDisplayTag', 'date', 'display');
Soll ein Tag für einen dedizierten DOM-Knoten registriert werden, so lässt sich dies in einer Tag-Implementierung wie folgt erreichen:
use APF\core\pagecontroller\Document;
class MyTag extends Document {
public function __construct() {
$this->addInstanceTagLib('VENDOR\..\SpecialDateDisplayTag', 'date', 'display');
}
}
Wird der <date:display />-Tag innerhalb des Projektes eingesetzt, so erzeugt der APF-Parser eine Instanz der Klasse DateDisplayTag. Wird er innerhalb von MyTag eingesetzt, so erzeugt der APF-Parser eine SpecialDateDisplayTag-Instanz.
Innerhalb eines Templates lassen sich eigene Tags mit Hilfe des <core:addtaglib />-Tag hinzufügen. Auch hier haben Sie die Möglichkeit, den Tag für alle Templates oder nur für den aktuelle DOM-Knoten zu registrieren. Details hierzu finden Sie in Kapitel Standard TagLibs.
<g:plusone></g:plusone>
Die Struktur eines Tags beschreibt die Klasse Document. Diese ist die Mutter aller Tag-Implementierungen des APF und definiert eine Reihe von Methoden, die innerhalb des Timing-Modells des Page-Controllers zu unterschiedlichen Zwecken aufgerufen werden.
Die Signatur de Klasse Document ist wie folgt (gekürzt):
class Document extends APFObject {
protected $objectId = null;
protected $parentObject = null;
protected $children = array();
protected $content;
protected static $knownTags = array();
protected $knownInstanceTags = array();
protected static $knownExpressions = array();
protected $data = array();
protected static $documentIndex = array();
public function __construct() {
}
public function onParseTime() {
}
public function onAfterAppend() {
}
public function transform() {
}
}
Den aufgeführten Elementen kommt dabei folgende Bedeutung zu:
foreach($this->children as $objectId => $DUMMY){
$this->content = str_replace(
'<'.$objectId.' />',
$this->children[$objectId]->transform(),
$this->content
);
}
Für die Transformation von Kind-Knoten können folgende Methoden genutzt werden:
Dieses Kapitel beschäftigt sich mit einfachen Tags. "Einfach" meint in diesem Zusammenhang einen Tag, der eine bestimmte Aufgabe erledigt, jedoch keine weitere Hierarchie erzeugt - sprich keine Kind-Tags besitzt.
Als Beispiel soll die Ausgabe der aktuellen Uhrzeit aus Kapitel 2 dienen. Hierzu muss der Tag lediglich zur Transformations-Zeit die Uhrzeit im gewünschten Format zurückgeben. Abhängigkeiten zu anderen Tags im Baum und die Notwendigkeit der Initialisierung bestehen nicht. Der Quellcode des Tags gestaltet sich damit wie folgt (Namespace: VENDOR\tags):
use APF\core\pagecontroller\Document;
class HourDisplayTag extends Document {
public function transform() {
return date($this->getAttribute('format'));
}
}
Innerhalb eines Templates lässt er sich wie anschließend gezeigt verwenden:
<core:addtaglib
class="VENDOR\tags\HourDisplayTag"
prefix="current"
name="date"
/>
<current:date format="H:i:s" />
Als "komplexer" Tag versteht das APF einen Tag mit weiteren - in der Hierarchie nicht eingeschränkten - Kind-Knoten.
Als Beispiel soll die Ausgabe einer Bildergalerie aus Kapitel 2 dienen. Dieser Tag definiert zunächst statischen Inhalt, der zur Ausgabe einer Überschrift und eines Einleitungstextes genutzt wird. Die Generierung der Ausgabe selbst wird vom <img:gallery />-Tag mit Hilfe eines Content-Providers, der durch den <gallery:datasource />-Tag definiert wird.
Die Basis-Struktur des Tags gestaltet sich wie folgt:
use APF\core\pagecontroller\Document;
use VENDOR\tags\GalleryDataSourceTag;
class GalleryTag extends Document {
public function __construct() {
Document::addTagLib(
'VENDOR\tags\GalleryDataSourceTag',
'gallery',
'datasource'
);
}
...
}
Im Konstruktor des Tags wird dem Parser der <gallery:datasource />-Tag bekannt gegeben. In diesem Fall setzen wir auf eine globale Registrierung. Damit kann der Tag unabhängig von der Hierarchie auch in anderen Templates eingesetzt werden.
Widmen wir uns nun zunächst der Konfiguration des Content-Providers. Hierzu spendieren wir dem <img:gallery />-Tag ein privates Feld und einen Setter, damit der <gallery:datasource />-Tag die Instanz übergeben kann:
use APF\core\pagecontroller\Document;
use VENDOR\..\ImageGalleryContentProvider;
class GalleryTag extends Document {
private $contentProvider;
...
public function setContentProvider(ImageGalleryContentProvider $provider) {
$this->contentProvider = $provider;
}
...
}
Der Provider selbst definiert sich durch folgendes Interface:
interface ImageGalleryContentProvider {
/**
* @return GalleryImage[]
*/
public function getImages();
}
class GalleryImage {
private $title;
private $description;
private $imageUrl;
public function __construct($title, $description, $imageUrl) {
$this->title = $title;
$this->description = $description;
$this->imageUrl = $imageUrl;
}
public function getDescription() {
return $this->description;
}
public function getImageUrl() {
return $this->imageUrl;
}
public function getTitle() {
return $this->title;
}
}
Gemäß unseres Beispieles ist der <gallery:datasource />-Tag dafür zuständig den gewünschten Provider zu erzeugen und dem GalleryTag mitzugeben. Dies lässt sich innerhalb der onAfterAppend()-Methode erledigen, da zu diesem Zeitpunkt der Vater-Knoten bekannt ist:
use APF\core\pagecontroller\Document;
use VENDOR\tags\GalleryTag;
class GalleryDataSourceTag extends Document {
public function onAfterAppend() {
$provider = $this->getServiceObject(
$this->getAttribute('class')
);
/* @var $parent GalleryTag */
$parent = $this->getParent();
$parent->setContentProvider($provider);
}
public function transform() {
return '';
}
}
In der Methode onAfterAppend() erzeugt das Tag den gewünschten Provider mit Hilfe des ServiceManager um der Instanz Kontext und Sprache des aktuellen Elements mitzugeben. Anschließend wird dem Vater-Element des DOM-Baumes - hier eine Instanz der Klasse GalleryTag wie mit dem Type-Hint angegeben wird - der Provider injiziert.
Die leer überschriebene transform()-Methode sorgt dafür, dass keine weiteren Kind-Elemente transformiert oder eine Ausgabe erzeugt wird. Dies ist im aktuellen Beispiel nicht notwendig.
Damit der GalleryDataSourceTag seine Arbeit verrichten kann muss der GalleryTag dafür sorgen, dass er auch vom Page-Controller erfasst und ausgeführt wird. Dazu bringt der Page-Controller bereits eine Parser-Methode mit, die in jeder von Document erbenden Klasse ausgeführt werden kann - Document::extractTagLibTags().
Zu welchem Zeitpunkt diese aufgerufen werden soll entscheiden zunächst Sie selbst. Beachten Sie dabei jedoch immer das Ablaufdiagramm des Page-Controller um den für den Anwendungsfall richtigen Zeitpunkt zu wählen. In unserem Fall ist lediglich wichtig, dass der Provider vor der Erzeugung der Inhalte ausgeführt wird. Damit sind theoretisch drei Möglichkeiten denkbar:
Üblicherweise schickt es sich für die Analyse von weiteren Kind-Strukturen die onParseTime()-Methode zu nutzen, da Sie damit sicherstellen, dass die weiteren Elemente im Baum ebenfalls die Möglichkeit haben, Kind-Knoten zeitlich korrekt zu erzeugen.
Um den GalleryDataSourceTag zu erzeugen und auszuführen, reicht folgender Quellcode:
use APF\core\pagecontroller\Document;
class GalleryTag extends Document {
...
public function onParseTime() {
$this->extractTagLibTags();
}
...
}
Alles Weitere erledigt der APF-Parser für Sie und an Hand des Template-Inhaltes.
Die Ausgabe der Bildergalerie erfolgt in der Methode transform(), die vom Page-Controller bei der Transformation des Baumes aufgerufen wird. Diese muss nun dafür sorgen, dass die Bilder, die vom Provider geliefert werden entsprechend ausgegeben werden.
Unter der Annahme, dass alle Bilder als ungeordnete Liste mit entsprechendem Platz für Beschreibungen ausgegeben werden, gestaltet sich die Methode transform() wie folgt:
use APF\core\pagecontroller\Document;
class GalleryTag extends Document {
...
public function transform() {
$images = $this->contentProvider->getImages();
$buffer = '<ul class="gallery-images">';
foreach ($images as $image) {
$buffer .= '<li>'
. '<img src="' . $image->getImageUrl() . '" alt="' . $image->getTitle() . '" />'
. '<p>' . $image->getDescription() . '</p>'
. '</li>';
}
$buffer .= '</ul>';
$this->setContent($this->getContent() . $buffer);
}
}
Mit der aktuellen Implementierung werden alle Bilder als Listen-Elemente nach dem im <img:gallery />-Tag vorhandenen statischen Inhalt angezeigt.
<{OBJECT_ID} />
Im Fall des <img:gallery />-Tags sollen die Kind-Tags keine Ausgabe erzeugen, was prinzipiell mit der GalleryDataSourceTag::transform()-Methode sichergestellt. Da der APF-Parser jedoch zur Positionierung der Ausgabe Platzhalter erzeugt, müssen diese in unserem Beispiel dennoch ersetzt werden. Dies kann durch folgende Erweiterung passieren:
use APF\core\pagecontroller\Document;
class GalleryTag extends Document {
...
public function transform() {
$images = $this->contentProvider->getImages();
$buffer = '<ul class="gallery-images">';
foreach ($images as $image) {
$buffer .= '<li>'
. '<img src="' . $image->getImageUrl() . '" alt="' . $image->getTitle() . '" />'
. '<p>' . $image->getDescription() . '</p>'
. '</li>';
}
$buffer .= '</ul>';
$this->setContent($this->getContent() . $buffer);
foreach ($this->children as $objectId => $DUMMY) {
$this->content = str_replace(
'<' . $objectId . ' />',
$this->children[$objectId]->transform(),
$this->content
);
}
}
}
Zur Implementierung von Tag-Logik stehen zahlreiche Methoden zur Verfügung. Diese umfassen das Parsen von Tag-Strukturen, das Traversieren des Baumes und das Manipulieren oder Auslesen von Tag-Informationen. Diese sind:
Weitere Methoden der Klasse Document oder der Basis-Klasse ihres aktuellen Tags finden Sie in der API-Dokumentation.
Der Parser des APF besitzt folgende Funktionalitäten:
<foo:bar>
...
<foo:bar>
...
</foo:bar>
...
<foo:bar />
...
</foo:bar>
<foo:bar>
...
<zig:zag>
...
<foo:bar />
...
</zig:zag>
...
</foo:bar>
Darüber hinaus implementiert der APF-Parser die Konzepte des APF Page-Controller und sorgt für den Aufbau des DOM-Baumes.
Der Tag-Parser des APF besitzt aus Performance-Gründen einigen Restriktionen gegenüber einem "echten" XML-Parser. Diese sind:
<form:recaptcha
name="my-captcha">
<recaptcha:getstring
...
/>
</form:recaptcha>
In diesem Kapitel finden Sie Anwendungsbeispiele für Tags, die Sie in Ihrer täglichen Arbeit mit dem APF als Vorlage für eigene Implementierungen bzw. als Coding-Guideline nutzen können. Als Daumenregel gilt:
Als Beispiel soll in diesem Kapitel der <html:text />-Tag implementiert werden. Dieser gibt einen sprachabhängigen Text aus, der an Hand eines Attributs definiert wird. Beispiel:
<html:text key="log-in.mousover.text" />
Die Implementierung des Tags beinhaltet die Verarbeitung des Attributs und die Ausgabe des Textes zur Transformationszeit. Dies kann mit folgender Implementierung bewerkstelligt werden:
use APF\core\pagecontroller\Document;
class TranslationTextTag extends Document {
public function transform() {
return gettext($this->getAttribute('key'));
}
}
Sollen die Attribute eines Tags bei der Generierung der Ausgabe einbezogen werden, kann die Methode getAttributesAsString() genutzt werden. Als Beispiel soll ein Tag genutzt werden, der die Ausgabe eines Bildes aus einer Medien-Datenbank an Hand einer externen ID erzeugt:
<html:img
key="IMG-12345"
width="100"
height="120"
alt="Diese Bild zeigt ein rotes Auto"
/>
Die zugehörige Implementierung kann wie folgt aussehen:
use APF\core\pagecontroller\Document;
class MAMImageTag extends Document {
public function transform() {
$width = $this->getAttribute('width', '50');
$height = $this->getAttribute('height', '50');
$key = $this->getAttribute('key');
$whiteList = array(
'alt',
'height',
'width',
'id',
'style',
'title'
);
return '<img src="'
. $this->getImageUrl($key, $width, $height) . '" '
. $this->getAttributesAsString($this->getAttributes(), $whiteList)
. ' />';
}
private function getImageUrl($key, $width, $height) {
...
}
}
In der obigen Code-Box wird die Methode getAttribute() dazu genutzt um die Werte der angegebenen Attribute auszulesen. Da einige der Attribute für die Ausgabe wiederverwendet werden sollen, wird der Methode getAttributesAsString() eine Liste an erlaubten Attributen - in diesem Fall kompatibel zur XHTML- bzw. HTML5-Spezifikation - mitgegeben. Die Ausgabe des Tags ist damit ein Image-Tag, der mit Hilfe der (HTML-)Attribute id, style und class formatiert werden kann.
Neben den Attributen eines Tags kann dieser auch einfachen und komplexen Inhalt definieren. Als Implementierungsbeispiel soll in diesem Kapitel folgender Tag genutzt werden:
<html:entityencode>nobody@example.com</html:entityencode>
Ausgabe des Tags soll eine in HTML-Entitäten codierte E-Mail-Adresse sein, um sie vor Bots oder Spidern zu schützen. Hierzu muss der Inhalt des Tags ausgelesen und im Rahmen der Transformation des Tags ausgegeben werden. Dies führt zu folgender Implementierung:
use APF\core\pagecontroller\Document;
use APF\tools\string\StringAssistant;
class EntityEncodeTag extends Document {
public function transform() {
return StringAssistant::encodeCharactersToHTML($this->getContent());
}
}
Mit der Methode getContent() kann auf den Inhalt des Tags zugegriffen werden. Dieser wird mit Hilfe des StringAssistant in HTML-Entitäten umgewandelt.
Wie im Kapitel 1 angesprochen erzeugt der Page-Controller aus den genutzten Templates und den darin enthaltenen Tags einen DOM-Baum. Das Verhalten ist - bis auf die Art der Erzeugung - mit dem eines Browsers vergleichbar, der ebenfalls aus dem angelieferten HTML einen DOM-Baum erzeugt und diesen grafisch aufbereitet.
Da Tags und (Document-)Controller Teil des Baumes sind haben Sie dort die Möglichkeit auf alle umliegenden Knoten zuzugreifen. Hierzu gibt es im Wesentlichen zwei Methoden:
Innerhalb eines Tags können Sie mit diesen Methoden direkt auf die umliegenden Elemente zugreifen, innerhalb eines (Document-)Controller beziehen Sie mit getDocument() eine Referenz auf den aktuellen DOM-Knoten und können ab diesem mit den oben genannten Methoden navigieren.
Als Beispiel für den Zugriff auf soll folgendes Template dienen:
<core:addtaglib
class="VENDOR\..\TemplateNameDisplayTag"
prefix="template"
name="display-name"
/>
<html:template name="test1">
...
</html:template>
<html:template name="test2">
...
<template:display-name />
...
</html:template>
Der TemplateNameDisplayTag hat dabei die Aufgabe, die Namen aller Templates auszugeben, die im Baum auf gleicher Ebene hängen wie sein Vater. Hierzu kann folgende Implementierung genutzt werden:
use APF\core\pagecontroller\Document;
class TemplateNameDisplayTag extends Document {
public function transform() {
$template = $this->getParent();
$grandFather = $template->getParent();
$nodes = $grandFather->getChildren();
$buffer = '<ul>';
foreach ($nodes as $objectId => $DUMMY) {
$buffer .= '<li>' . $nodes[$objectId]->getAttribute('name') . '</li>';
}
return $buffer . '</ul>';
}
}
Ein weiterführendes Beispiel finden Sie im Forum unter Placeholder über Taglib füllen.
Die vorangegangenen Kapitel haben sich mit dem Zugriff auf Attribute, der Ausgabe von Attributen und dem Zugriff und der Verarbeitung von Tag-Inhalten beschäftigt. In diesem Abschnitt beschäftigen wir uns mit komplexeren Tags, die weitere Strukturen definieren und Inhalte sowie Attribute in die Abbildung ihrer Logik einbeziehen.
Als Beispiel soll uns ein Tag dienen, der Navigationsknoten vom Typ NavigationNode ausgibt:
<core:addtaglib
class="APF\extensions\navigation\pres\tags\NavigationNodeTag"
prefix="navi"
name="template"
/>
<navi:template id="main-navi"><!-- NavigationNodeTag -->
<navi:item status="active"><!-- NavigationItemTag -->
<li class="active">
<item:content/><!-- ItemTemplateContentTag -->
</li>
</navi:item>
<navi:item status="inactive">
<li>
<item:content/>
</li>
</navi:item>
<ul id="main-navigation">
<navi:content/><!-- NavigationContentTag -->
</ul>
</navi:template>
Der Tag wird in einem (Document-)Controller gefüllt und stellt die Liste der Kinder des übergebenen Navigationsknotens gemäß den definierten Formatierungen dar. Dabei beschreiben die <navi:item />-Tags die aktiven und inaktiven Zustände der Navigationsknotens und mit Hilfe von <navi:content/> lässt sich die Ausgabe in ein HTML-Gerüst packen. Der Tag <item:content/> platziert die Ausgabe eines konkreten Knotens und kann ebenfalls mit eigenem HTML versehen werden. Zur Erläuterung der Tag-Hierarchie sind die Namen der korrespondierenden Tags als HTML-Kommentar angefügt.
Zur Befüllung des <navi:template />-Tags in einem Template lässt sich folgender Controller nutzen:
use APF\core\pagecontroller\BaseDocumentController;
use APF\extensions\navigation\biz\SimpleNavigationNode;
class NavigationTagExampleController extends BaseDocumentController {
public function transformContent() {
$root = new SimpleNavigationNode(null, null, null);
$levelOne = new SimpleNavigationNode('Level 1', '#');
$root->setChildren(array(
clone $levelOne->setInactive(),
clone $levelOne->setActive(),
clone $levelOne->setInactive()
));
$navi = $this->getDocument()->getChildNode('id', 'main-navi', 'NavigationNodeTag');
$navi->setNode($root);
}
}
Die folgende Code-Box zeigt nun die Implementierung der Tags:
use APF\core\pagecontroller\Document;
interface NavigationNode {
public function getLabel();
public function getUrl();
public function isActive();
public function getParent();
public function getChildren();
}
class SimpleNavigationNode implements NavigationNode {
private $label;
private $url;
private $isActive = false;
private $parent;
private $children = array();
public function __construct($label, $url) {
$this->label = $label;
$this->url = $url;
}
public function getLabel() {
return $this->label;
}
public function getUrl() {
return $this->url;
}
public function isActive() {
return $this->isActive;
}
public function setActive() {
$this->isActive = true;
return $this;
}
public function setInactive() {
$this->isActive = false;
return $this;
}
public function getParent() {
return $this->parent;
}
public function getChildren() {
return $this->children;
}
public function setParent(NavigationNode $node) {
$this->parent = $node;
}
public function setChildren(array $nodes) {
$this->children = $nodes;
}
}
class NavigationNodeTag extends Document {
private $node;
public function __construct() {
self::addTagLib('EXAMPLE\navigation\pres\taglibs\NavigationItemTag', 'navi', 'item');
self::addTagLib('EXAMPLE\navigation\pres\taglibs\NavigationContentTag', 'navi', 'content');
}
public function setNode(NavigationNode $node) {
$this->node = $node;
}
public function onParseTime() {
$this->extractTagLibTags();
}
public function transform() {
$buffer = '';
$navigationNodes = $this->node->getChildren();
if (count($navigationNodes) > 0) {
foreach ($navigationNodes as $node) {
$buffer .= $this
->getTemplate($node->isActive() ? 'active' : 'inactive')
->getOutput($node);
}
}
$content = $this->getContent();
$children = $this->getChildren();
foreach ($children as &$child) {
if ($child instanceof NavigationContentTag) {
// fill the navi:content place holder if we get him
$content = str_replace('<' . $child->getObjectId() . ' />', $buffer, $content);
} else {
// replace parser marker to avoid direct tag output
$content = str_replace('<' . $child->getObjectId() . ' />', '', $content);
}
}
return $content;
}
private function getTemplate($status) {
return $this->getChildNode('status', $status, 'NavigationItemTag');
}
}
class NavigationItemTag extends Document {
public function __construct() {
self::addTagLib('EXAMPLE\navigation\pres\taglibs\ItemTemplateContentTag', 'item', 'content');
}
public function onParseTime() {
$this->extractTagLibTags();
}
public function getOutput(NavigationNode $node) {
$content = $this->getContent();
$children = $this->getChildren();
foreach ($children as &$child) {
if ($child instanceof ItemTemplateContentTag) {
// fill the item:content place holder if we get him
$content = str_replace('<' . $child->getObjectId() . ' />', $child->setNode($node)->transform(), $content);
} else {
// replace parser marker to avoid direct tag output
$content = str_replace('<' . $child->getObjectId() . ' />', '', $content);
}
}
return $content;
}
public function transform() {
return '';
}
}
class ItemTemplateContentTag extends Document {
private $node;
public function setNode(NavigationNode $node) {
$this->node = $node;
return $this;
}
public function transform() {
if ($this->node === null) {
return '';
}
return '<a href="' . $this->node->getUrl() . '">' . $this->node->getLabel() . '</a>';
}
}
class NavigationContentTag extends Document {
}
Sofern Sie weitere Informationen benötigen, können Sie folgende Threads aus dem Forum nutzen:
Im Wiki finden sich folgende Seiten zu den APF-Tags:
Die Entwicklung des APF wird von JetBRAINS mit PHPStorm-Lizenzen unterstützt und wir sind überzeugt davon, dass PHPStorm die Qualität nachhaltig steigert. Benutzen auch Sie PHPStorm!
Proud to useIntelligent PHP IDE for coding, testing and debugging with pleasureUm unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen finden Sie in den Datenschutzrichtlinien.