Auf dieser Dokumentationsseite werden Funktionen des Frameworks besprochen die für spezielle Anwendungsfälle oder komplexere Designs verwendet werden können.
Das Iterator-Tag hilft Ihnen die Ausgabe von Listen von Objekten oder assoziativen Arrays zu bewerkstelligen. Die Tag-Definition in einer Template-Datei definiert dabei die Formatierung der Ausgabe, die Daten erhält der Iterator im Controller.
Der Iterator-Tag ist wie folgt aufgebaut:
Die Definition des Tags gestaltet sich wie folgt:
<html:iterator name="">
...
[<html:placeholder name="" />]
[<html:getstring namespace="" config="" entry="" />]
[<core:addtaglib class="" prefix="" name="" />]
...
<iterator:item [getter=""]>
...
<html:placeholder name="" />
[<html:getstring namespace="" config="" entry="" />]
...
</iterator:item>
...
[<iterator:fallback>
...
[<html:placeholder name="" />]
[<html:getstring namespace="" config="" entry="" />]
...
</iterator:fallback>]
...
</html:iterator>
Innerhalb eines <iterator:item />-Tags können beliebig viele Platzhalter (<html:placeholder />) und HTML-Elemente definiert werden.
Beschreibung der Attribute:[A-Za-z0-9]
)
[A-Za-z0-9_]
)
[A-Za-z0-9]
)
Details zur Verwendung des <iterator:item />- und <iterator:fallback />-Tags entnehmen Sie bitten den folgenden Kapiteln.
Der Iterator kann mit zwei Arten von Inhalten umgehen: assoziativen Arrays und Objekt-Listen. Aus Gründen der Flexibilität bei der Erstellung des Templates innerhalb des <iterator:item />-Tags empfiehlt sich die Nutzung von Objekt-Listen (siehe Kapitel 1.2.2).
Zur Ausgabe von Listen beherrscht der Iterator die Verarbeitung von assoziativen Arrays als Daten-Strukturen für ein <iterator:item />. Zur Ausgabe einer Produkt-Liste der Form
<html:iterator name="products-list-simple">
<h2>Produkte</h2>
<p>
Die folgende Liste stellt unsere Produkte dar:
</p>
<ul>
<iterator:item>
<li>
<h3><html:placeholder name="name" /></h3>
<img src="<html:placeholder name="img" />" />
<span><html:placeholder name="price" /></span>
</li>
</iterator:item>
</ul>
</html:iterator>
erwartet der Iterator folgende Datenstruktur:
class IteratorTestController extends BaseDocumentController {
public function transformContent() {
$data = [
[
'name' => 'Günstiges Mobiltelefon',
'price' => '150€',
'img' => 'cheapo.png'
],
[
'name' => 'Mittelklasse Mobiltelefon',
'price' => '350€',
'img' => 'mid-range.png'
],
[
'name' => 'Premium Mobiltelefon',
'price' => '550€',
'img' => 'premium.png'
]
];
$iterator = $this->getIterator('products-list-simple');
$iterator->fillDataContainer($data);
$iterator->transformOnPlace();
}
}
Die Methode fillDataContainer() liefert die Instanz des gewünschten Iterators zurück, der im Dokument definiert wurde, mit Hilfe von transformOnPlace() wird der Iterator dargestellt.
Die innerhalb des <iterator:item />-Tag definierten Platzhalter (z.B. <html:placeholder name="name" />) referenzieren auf die in der Datenstruktur enthaltenen Werte. Der Name des Schlüssels muss dabei mit den Wert des Attributs name übereinstimmen. Nicht gefundene Werte werden mit null ersetzt.
Neben der klassichen Definition von Platzhaltern lässt sich im <iterator:item />-Tag auch die erweiterte Template-Syntax zur Definition von Platzhaltern einsetzen:
<html:iterator name="products-list-simple">
<h2>Produkte</h2>
<p>
Die folgende Liste stellt unsere Produkte dar:
</p>
<ul>
<iterator:item>
<li>
<h3>${name}</h3>
<img src="${img}" />
<span>${price}</span>
</li>
</iterator:item>
</ul>
</html:iterator>
Details entnehmen Sie bitte dem Kapitel Templates.
<iterator:item>
<li>
<h3>${item['name']}</h3>
<img src="${item['img']}" />
<span>${item['price']}</span>
</li>
</iterator:item>
Der Zugriff auf Objekt-Listen verhält sich sehr ähnlich wie der im Kapitel 1.2.1 beschrieben Modell. Unterschied ist dabei, dass es sich beim Inhalt der Listen um Instanzen von beliebigen Objekten handelt.
Zur Ausgabe einer Produkt-Liste der Form
<html:iterator name="products-list-objects">
<h2>Produkte</h2>
<p>
Die folgende Liste stellt unsere Produkte dar:
</p>
<ul>
<iterator:item>
<li>
<h3><html:placeholder getter="getName" /></h3>
<img src="<html:placeholder getter="getImg" />" />
<span><html:placeholder getter="getPrice" /></span>
</li>
</iterator:item>
</ul>
</html:iterator>
erwartet der Iterator folgende Datenstruktur:
class Article {
private $name;
private $price;
private $img;
public function __construct($name, $price, $img) {
$this->name = $name;
$this->price = $price;
$this->img = $img;
}
public function getImg() {
return $this->img;
}
public function getName() {
return $this->name;
}
public function getPrice() {
return $this->price;
}
}
class IteratorTestController extends BaseDocumentController {
public function transformContent() {
$data = [
new Article('Günstiges Mobiltelefon', '150€', 'cheapo.png'),
new Article('Mittelklasse Mobiltelefon', '350€', 'mid-range.png'),
new Article('Premium Mobiltelefon', '550€', 'premium.png')
];
$iterator = $this->getIterator('products-list-objects');
$iterator->fillDataContainer($data);
$iterator->transformOnPlace();
}
}
Die Methode getIterator() liefert die Instanz des gewünschten Iterators zurück, der im Dokument definiert wurde, mit Hilfe von transformOnPlace() wird der Iterator dargestellt.
Die innerhalb des <iterator:item />-Tag definierten Platzhalter (z.B. <html:placeholder name="name" />) referenzieren auf die in den Objekten enthaltenen Werte. Der Name der Methode wird im Attribut getter des <iterator:item />-Tag definiert. Nicht gefundene Methoden führen zu einer Exception.
Das Auslesen eines Attributes eines Objektes kann auf zwei Arten stattfinden:
<html:iterator name="...">
<iterator:item getter="getProperty">
<html:placeholder name="DisplayName" />
</iterator:item>
</html:iterator>
public function getProperty($name)
<iterator:item>
<li>
<h3>${item->getName()}</h3>
<img src="${item->getImg()}" />
<span>${item->getPrice()}</span>
</li>
</iterator:item>
In einem Document-Controller stehen Ihnen dann folgende Methoden zur Verfügung, mit der Sie den Iterator nutzen lässt:
class ListController extends BaseDocumentController {
public function transformContent() {
// Iterator aus dem aktuellen Dokument beziehen
$iterator = $this->getIterator('...');
// Daten an den Iterator übergeben (Liste mit assoziativen Arrays oder Objekten)
$iterator->fillDataContainer(array(
...
));
// Ausgeben des Iterators wo er im Template definiert wurde, ...
$iterator->transformOnPlace();
// ... oder den Inhalt in einen Platzhalter einsetzen
$this->setPlaceHolder('...', $iterator->transformIterator());
}
}
Sollen die Einträge einer Liste bzw. Tabelle nummeriert werden, können Sie innerhalb eines <iterator:item />-Tags einen Platzhalter definieren der den Namen IterationNumber trägt. Beispiel:
<html:iterator name="">
<iterator:item [getter=""]>
<html:placeholder name="IterationNumber">
</iterator:item>
</html:iterator>
Bei Bedarf kann sie Start-Position der Zählvariable kann im Controller manipuliert werden. Hierzu lässt sich die Methode HtmlIteratorTag::setIterationNumber() nutzen:
class ListController extends BaseDocumentController {
public function transformContent() {
$iterator = $this->getIterator('...');
$iterator->fillDataContainer(array(
...
));
$iterator->setIterationNumber(5);
$iterator->transformOnPlace();
}
}
Zu weiteren Optionen der Manipulation, ziehen Sie bitte Kapitel 1.3.3 zu Rate.
Nutzt man das Iterator-Tag in Verbindung mit dem Pager, sollte die Nummerierung auf einer anderen Seite nicht wieder bei 1 beginnen, sondern fortgesetzt werden. Um dies zu erreichen, muss für den <html:iterator />-Tag das Attribut pager definiert werden. Als Wert muss das pager-Attribut den Namen einer Sektion der Pager-Konfigurationsdatei erhalten.
Im folgenden Beispiel würde die Iterator-TagLib die Konfiguration /APF/config/modules/pager/{ENVIRONMENT}_pager.ini aufrufen und die Konfigurations-Werte der Sektion PagerExample verwenden:
<html:iterator name="" pager="PagerExample">
<iterator:item [getter=""]>
<html:placeholder name="IterationNumber">
</iterator:item>
</html:iterator>
Bei der Ausgabe von Listen ist häufig auch die Darstellung eines Alternativ-Inhalts gefragt. Diesen Anwendungsfall unterstützt der Iterator über das <iterator:fallback />-Tag. Dieses wird ausgegeben, sofern dem Iterator keine Inhalte zur Ausgabe zur Verfügung stehen.
Zur Steuerung der Ausgabe stehen zwei Optionen zur Verfügung:
Der Modus kann über da Attribut fallback-mode des <html:iterator />-Tag gesteuert werden.
Der folgende Code-Block zeigt die Ausgabe einer Produkt-Liste inkl. Alternativ-Inhalt:
<html:iterator name="products-list-simple" fallback-mode="replace">
<h2>Produkte</h2>
<p>
Die folgende Liste stellt unsere Produkte dar:
</p>
<ul>
<iterator:item>
<li>
<h3><html:placeholder name="name" /></h3>
<img src="<html:placeholder name="img" />" />
<span><html:placeholder name="price" /></span>
</li>
</iterator:item>
</ul>
<iterator:fallback>
<h2>Keine Produkte verfügbar</h2>
<p>
Leider sind aktuell keine Produkte erhältlich.
Bitte besuchen Sie uns in <html:placeholder name="count" /> Tagen wieder.
</p>
</iterator:fallback>
</html:iterator>
Solange Produkte verfügbar sind, wird eine Liste von Produkten ausgegeben, sofern keine Produkte erhältlich sind, wird dir Liste durch den Alternativ-Inhalt ersetzt.
Die Alternativ-Inhalte lassen sich wie vom <html:template />-Tag gewohnt anpassen. Der folgende Code-Block zeigt Ihnen, wie Sie den Platzhalter im <iterator:fallback />-Tag mit der gewünschten Anzahl an Tagen füllen können:
class ProductsController extends BaseDocumentController {
public function transformContent() {
$iterator = $this->getIterator('products-list-simple');
...
$fallback = $iterator->getFallbackContent();
$fallback->setPlaceHolder('count', '3');
$iterator->transformOnPlace();
}
}
Der Iterator stellt für jeden Schleifen-Durchlauf mehrere Status-Variablen zur Verfügung. Diese können zur direkten Ausgabe genutzt werden oder in eigenen Tags ausgewertet werden. Die Informationen lassen sich über das Data-Attribut status abfragen, das eine Instanz der Klassen APF\tools\html\taglib\IteratorStatus beinhaltet. Dieses verfügt über folgende Methoden:
class IteratorStatus {
public function getCssClass() {
return $this->cssClass;
}
public function isFirst($asString = false) {
return $asString === false ? $this->isFirst : $this->convertToString($this->isFirst);
}
public function isLast($asString = false) {
return $asString === false ? $this->isLast : $this->convertToString($this->isLast);
}
public function getItemCount() {
return $this->itemCount;
}
public function getCounter() {
return $this->counter;
}
}
Die Methoden lassen sich wie folgt für die Generierung der Ausgabe nutzen:
Das folgende Beispiel zeigt die Ausgabe einer Produkt-Liste, die das Status-Objekt zur Formatierung der Ausgabe nutzt:
<html:iterator
name="products-list-objects-extended"
first-element-css-class="prd-first separator"
middle-element-css-class="prd-center"
last-element-css-class="prd-last">
<h2>Produkte</h2>
<p>
Die folgende Liste stellt unsere Produkte dar:
</p>
<ul>
<iterator:item>
<li
class="${status->getCssClass()}"
data-item-count="${status->getItemCount()}"
data-is-first="${status->isFirst(true)}"
data-is-last="${status->isLast(true)}">
<h3>(${item->getCounter()}) ${item->getName()}</h3>
<img src="${item->getImg()}" />
<span>${item->getPrice()}</span>
</li>
</iterator:item>
</ul>
</html:iterator>
Die entsprechende HTML-Ausgabe gestaltet sich daraus wie folgt (gekürzt):
<ul>
<li
class="prd-first separator"
data-item-count="3"
data-is-first="1"
data-is-last="0">
<h3>Cheap mobile phone</h3>
<img src="cheapo.png"/>
<span>150€</span>
</li>
<li
class="prd-center"
data-item-count="3"
data-is-first="0"
data-is-last="0">
<h3>Mid-range mobile phone</h3>
<img src="mid-range.png"/>
<span>350€</span>
</li>
<li
class="prd-last"
data-item-count="3"
data-is-first="0"
data-is-last="1">
<h3>Premium mobile phone</h3>
<img src="premium.png"/>
<span>550€</span>
</li>
</ul>
Für die Ausgabe von verschachtelten Listen lassen sich Iteratoren auch mehrfach schachteln. Der folgende Code-Block definiert eine verschachtelte Datenstruktur, die eine Liste mit Frauen- und Männer-Namen mit jeweils zwei Beispielen enthält:
use APF\core\pagecontroller\BaseDocumentController;
class IteratorStackingController extends BaseDocumentController {
public function transformContent() {
$iterator = $this->getIterator('names');
$iterator->fillDataContainer([
[
'name' => 'Women names:',
'list' => [
['name' => 'Maria'],
['name' => 'Theodora']
]
],
[
'name' => 'Men names:',
'list' => [
['name' => 'John'],
['name' => 'George']
]
]
]);
}
}
Möchten Sie die Namen mit Hilfe einer verschachtelten Liste ausgeben, so lässt sich dafür folgender Template-Code nutzen:
<html:iterator name="names" transform-on-place="true">
<ul>
<iterator:item>
<li>
<item:fill-iterator name="examples" data="item['list']"/>
(${status->getCounter()}) ${item['name']}
<ul>
<html:iterator name="examples" transform-on-place="true">
<iterator:item>
<li>(${status->getCounter()}) ${item['name']}</li>
</iterator:item>
</html:iterator>
</ul>
</li>
</iterator:item>
</ul>
</html:iterator>
Der <item-fill-iterator />-Tag sorgt analog der fillDataContainer()-Methode dafür, dass der Iterator - in diesem Fall der geschachtelte Iterator examples - mit Daten befüllt wird. Dabei bedient er sich aus den Inhalten des aktuellen Schleifendurchlaufs und injiziert die Inhalte des Attributs list in den geschachtelten Iterator zur Ausgabe.
Das Attribut name des <item-fill-iterator />-Tags referenziert auf den geschachtelten Iterator - in diesem Fall examples - und das Attribut data wird zur Evaluierung des Inhalts genutzt, der in den geschachtelten Iterator ijiziert wird. Die Syntax folgt der im Kapitel Erweiterte Template-Funktionen beschriebenen erweiterten Templating-Syntax.
Die Ausgabe der im Controller definierten Liste gestaltet sich wie folgt:
<ul>
<li>
(1) Women names:
<ul>
<li>(1) Maria</li>
<li>(2) Theodora</li>
</ul>
</li>
<li>
(2) Men names:
<ul>
<li>(1) John</li>
<li>(2) George</li>
</ul>
</li>
</ul>
Die Mediastream-Tags ermöglichen es dem Entwickler, Ressourcen zur Gestaltung der GUI direkt im Namespace des Moduls abzulegen und daraus auszuliefern. Hierzu stellt das Framework einerseits eine abstrakte TagLib-Implementierung und einige konkrete TagLibs zur Verfügung, die eine Medien-URL generieren, andererseite eine allgemeingültig verwendbare FrontController-Action, die die adressierten Medien ausliefert.
Um die Tags einsetzen zu können, muss sichergestellt sein, dass für die Action, die mit der Auslieferung betraut ist eine validie Konfiguration für den aktuellen Applikations-Kontext existiert. Die Konfiguration wird dabei unter
/APF/config/tools/media/actions/{CONTEXT}/{ENVIRONMENT}_actionconfig.ini
erwartet. Der Inhalt der Datei kann der nachfolgenden Code-Box entnommen werden:
[streamMedia]
ActionClass = "APF\tools\media\actions\StreamMediaAction"
Eine Beispieldatei findet sich ebenfalls in der apf-configpack-*-Release-Datei unter tools/media/actions/.
Die folgende Code-Box zeigt die Anwendung innerhalb eines Formulars:
<html:form name="TestFormular">
<img src="<html:mediastream
namespace="APF\modules\mymodule\pres\images"
filename="phone_icon.png"
/>" alt="" />
<form:text name="phonenumber" />
<br />
<form:button name="send" value="Absenden" />
</html:form>
Wie dem Beispiel zu entnehmen ist, erwartet der <html:mediastream />-Tag folgende Attribute:
[A-Za-z0-9:]
)
[A-Za-z0-9_.-]
)
<img src="<html:mediastream
namespace="APF\modules\mymodule\pres\images"
filename="phone_icon.png"
id="PhoneIcon"
/>"
alt=""
/>
class ExampleController extends BaseDocumentController {
public function transformContent(){
$mediaStreamTag = $this->getMediaStreamTagByID('PhoneIcon');
$mediaStreamTag->setAttribute($mediaStreamTag->getAttribute('namespace').'\\'.$this->getContext());
}
private function getMediaStreamTagByID($id){
$children = $this->getDocument()->getChildren();
foreach($children as &$child){
if($child instanceof MediaInclusionTag){
return $child;
}
}
throw new InvalidArgumentException('No media stream tag contained within the current document!');
}
}
class ExampleController extends BaseDocumentController {
public function transformContent() {
$FileTemplate = $this->getTemplate('file');
$mediaStreamTag = $this->getMediaStreamTagByID('FileIconID', $FileTemplate);
$mediaStreamTag->setAttribute('extension', 'png');
$mediaStreamTag->setAttribute('filebody', 'dateinameOhneEndung');
}
private function getMediaStreamTagByID($id, TemplateTag &$template) {
$children = $template->getChildren();
foreach ($children as &$child) {
if ($child instanceof MediaInclusionTag && $child->getAttribute('id') == $id) {
return $child;
}
}
throw new InvalidArgumentException('No media stream tag contained within the current template "'
. $template->getAttribute('name') . '"!');
}
}
Die zur Auslieferung der Bilder eingesetzte Front-Controller-Action StreamMediaAction bietet die Möglichkeit, erlaubte Datei-Typen und deren MIME-Typen explizit zu konfigurieren. Damit ist es möglich die Standard-Werte zu überschreiben um weniger oder mehr Typen zuzulassen. Im Standard-Setup sind folgende Endungen erlaubt:
Um die vorhandenen Werte neu zu definieren muss die Konfigurations-Datei {ENVIRONMENT}_allowed_extensions.ini unter dem Pfad APF\tools\media\{CONTEXT} angelegt werden. Die erlaubten Endungen können dann in der Form
[Default]
jpg = "image/jpg"
xml = "text/xml"
psd = "application/psd"
definiert werden.
In komplexeren Applikationen ist es oft notwendig, die durch <*:importdesign />-Tags definierten Views dynamisch füllen zu können. Vielfach möchte der Entwickler in aufwändigeren Strukturen die Informationen des Applikationsmodels verwenden. Um dies uneingeschränkt zu ermöglichen und eine Applikationssteuerung aus der Business-Schicht zu ermöglichen, wurde das Framework mit einem generischen importdesign-Tag ausgestattet, der es erlaubt sowohl den Namen des Templates aus auch den Namespace desselben dynamisch aus einem Model-Objekt zu beziehen.
Die Signatur des generischen Tags gestaltet sich dabei wie folgt:
<generic:importdesign
model-class=""
model-mode="NORMAL|SINGLETON|SESSIONSINGLETON"
namespace-param=""
template-param=""
[get-method=""]
[dependent-action-namespace=""
dependent-action-name=""
[dependent-action-params=""]]
/>
[A-Za-z0-9_\]
)
NORMAL|SINGLETON|SESSIONSINGLETON
)
[A-Za-z0-9_.-]
)
[A-Za-z0-9_.-]
)
[A-Za-z0-9_]
)
Um das Tag anwenden zu könen, muss dieses zunächst via
<core:addtaglib
class="APF\tools\html\taglib\GenericImportTemplateTag"
prefix="generic"
name="importdesign"
/>
bekannt gemacht werden.
Hinweise:Aus einer Diskussion über wiederverwendbare Template-Fragmente (z.B. Formulare) entstand die Idee, eine TagLib zu entwerfen, die Inhalte aus einem beliebigen Template in den aktuellen Gültigkeitsbereich zu importieren. Durch die generische DOM-Struktur der GUI-Elemente des Frameworks ist dies auf sehr einfache Weise möglich.
Um die Funktion allgemeingültig zur Verfügung zu stellen, wurde im 1.8er-Zweig der <core:appendnode />-Tag hinzugefügt, der beliebige Templates "importieren" kann. Der Tag erwartet - ähnlich dem importdesign-Tag - die statischen Attribute namespace und template.
Um ein wiederverwendbares Template einbinden zu können, muss das Tag im gewünschten Template wir folgt platziert werden:
<core:appendnode
namespace="..."
template="..."
[includestatic="true"]
/>
Soll beispielweise ein Template zur Ausgabe eines Domänen-Objekts in mehreren View-Templates eingesetzt werden, so schickt es sich, dieses in einem eigenen Template (Namespace: APF\sites\testsite\pres\templates\generic; Template: generic_templates) zu definieren. Die Definition kann dabei folgende Gestalt haben:
<html:template name="ReusableTemplate">
...
<html:placeholder name="DisplayName">
...
</html:template>
Um das Template in einem anderen verwenden zu können, muss die Template-Datei wie folgt in die bestehende eingebunden werden:
<core:appendnode
namespace="APF\sites\testsite\pres\templates\generic"
template="generic_templates"
/>
Die Verwendung der durch das <core:appendnode />-Tag eingebundenen Elements gestaltet sich identisch zur bisherigen Vorgehensweise, da die Elemente in den Gültigkeitsbereich des gewünschten Templates importiert werden. Damit können weiterhin die im Document-Controller zur Verfügung stehenden Methoden (z.B. getTemplate()) verwendet werden.
Das im Kapitel 4.1 aufgezeigte Template könn wie auch bisher mit
$tmpl = $this->getTemplate('ReusableTemplate');
adressiert werden.
Das Parsen des eingebundenen Templates erfolgt identisch zu den per <core:importdesign />-Tag eingebundenen Template-Dateien.
Die <core:appendnode />-TagLib legt im Ursprungstemplate Marker-Tags an, damit die transformOnPlace()-Methoden genutzt werden können. Bitte beachten Sie, dass die eingebundenen Kinder in der Reihenfolge der Definition im zusätzlichen Template eingebunden werden!
Sofern auch statischer Inhalt des eingebundenen Templates wie z.B.
<div class="formattingContainer">
<html:template name="ReusableTemplate">
...
</html:template>
</div>
übernommen werden soll, muss das Attribut includestatic auf den Wert true gestellt werden.
Um 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.