Kommentar-Funktion

1. Einleitung

Dieses Tutorial möchte zeigen, wie das Kommentar-Modul technisch aufgebaut ist und welchen Komponenten welche Funktion erfüllen. Das Ergebnis findet sich am Ende dieser Seite zum Ausprobieren.

Es soll zudem verdeutlicht werden, welche Paradigmen und Design-Pattern Anwendung finden. Dazu werden die unter Grundlagen bereits vermittelten Design-Pattern praktisch für diesen Anwendungsfall aufbereitet.

2. Implementierung der Software

Die Kommentar-Funktion besteht aus Sicht eines Anwenders aus zwei Teilen: Ausgabe und Formular. Aus Sicht eines Entwicklers aus der Ausgabe, dem Formular, einer Datenbank und der zugehörigen Logik, die das Auslesen der Daten und das Füllen der Datenbank organisiert.

Diesen Komponenten wird im Folgenden Rechnung getragen. Die Struktur der Software wird gemäß einer Schichten-Architektur in drei Bereiche aufgeteilt.

Für das Vorgehen bei der Implementierung der Software gibt es unterschiedliche Ansätze: top-down bzw. bottom-up. In diesem Fall nutzen wir letzteres.

2.1. Struktur des Moduls

Zunächst soll die Ordner- bzw. Namespace-Struktur der Software angelegt werden. Da es sich bei der Kommentar-Funktion gemäß Grundlagen um ein Stück Software handelt, das in mehreren Projekten (=Webseiten) eingesetzt werden kann, wird das Modul im Ordner /APF/modules abgelegt wird.

Für die Implementierung von eigenen Modulen wird davon abgeraten, den Ordner modules zu nutzen, da sonst eigene Software beim Update u.U. überschrieben wird. Das das Kommentar-Modul ein Teil des APF ist, ist diese Vorgehensweise für dieses Tutorial in Ordnung.

Das Modul erhält den Namen comments, der gleichzeitig auch der Ordner-Name ist. Zur Strukturierung der Software werden nun die Ordner data, biz und pres angelegt. Innerhalb von pres fügen wir jeweils einen Ordner für die Controller (controller) und die View-Komponenten (templates) ein. Es ergibt sich somit folgende Ordnerstruktur

Code
/APF /config /core /extensions /modules /comments /biz /data /pres /css /controller /templates /tools

Da für einige Komponenten Konfigurationen erforderlich sind, existiert in der Übersicht zusätzlich ein Ordner config. Der Ordner css ist für die mitgelieferten Stylesheets gedacht.

2.2. Domain-Objekt

Als erste Klasse legen wir das von allen Schichten genutzte Domain-Objekt ArticleComment an. Dazu wird die Datei ArticleComment.php im Ordner /APF/modules/comments/biz erstellt und mit folgenden Inhalt gefüllt:

PHP-Code
namespace APF\modules\comments\biz class ArticleComment { private $id = null; private $name; private $email; private $comment; private $date; private $time; private $categoryKey; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getEmail() { return $this->email; } public function setEmail($email) { $this->email = $email; } public function getComment() { return $this->comment; } public function setComment($comment) { $this->comment = $comment; } public function getDate() { return $this->date; } public function setDate($date) { $this->date = $date; } public function getTime() { return $this->time; } public function setTime($time) { $this->time = $time; } public function getCategoryKey() { return $this->categoryKey; } public function setCategoryKey($categoryKey) { $this->categoryKey = $categoryKey; } }

2.3. Datenschicht

Die Datenschicht besteht aus einem DataMapper, der zunächst nur eine Lese-Funktion erhält.

An dieser Stelle sei vorweggenommen, dass zur Ausgabe einer blätterbaren Ansicht der Pager zum Einsatz kommt. Dieser setzt voraus, dass eine entsprechende Datenschicht-Komponente eine Methode zum Laden von Einträgen über eine eindeutige Id besitzt. Aus diesem Grund wird unser Mapper mit der Methode loadArticleCommentByID() ausgestattet.

Zunächst erstellen für unseren Mapper eine Datei mit dem Namen commentMapper.php unter /APF/modules/comments/data und sehen folgenden Inhalt vor:

PHP-Code
namespace APF\modules\comments\data; use APF\core\database\AbstractDatabaseHandler; use APF\core\database\ConnectionManager; use APF\core\pagecontroller\APFObject; use APF\modules\comments\biz\ArticleComment; class ArticleCommentMapper extends APFObject { public function loadArticleCommentByID($commentId) { $SQL = & $this->getConnection(); $select = 'SELECT ArticleCommentID, Name, EMail, Comment, Date, Time FROM article_comments WHERE ArticleCommentID = \'' . $commentId . '\';'; $result = $SQL->executeTextStatement($select); return $this->mapArticleComment2DomainObject($SQL->fetchData($result)); } public function saveArticleComment(ArticleComment $comment) { $conn = & $this->getConnection(); if ($comment->getId() == null) { $insert = 'INSERT INTO article_comments (Name, EMail, Comment, Date, Time, CategoryKey) VALUES (\'' . $conn->escapeValue($comment->getName()) . '\',\'' . $conn->escapeValue($comment->getEmail()) . '\',\'' . $conn->escapeValue($comment->getComment()) . '\',CURDATE(),CURTIME(),\'' . $comment->getCategoryKey() . '\');'; $conn->executeTextStatement($insert); } } private function &getConnection() { /* @var $cM ConnectionManager */ $cM = & $this->getServiceObject('APF\core\database\ConnectionManager'); $config = $this->getConfiguration('APF\modules\comments', 'comments.ini'); $connectionKey = $config->getSection('Default')->getValue('Database.ConnectionKey'); if ($connectionKey == null) { throw new \InvalidArgumentException('[ArticleCommentMapper::getConnection()] The module\'s ' . 'configuration file does not contain a valid database connection key. Please ' . 'specify the database configuration according to the example configuration files!', E_USER_ERROR); } return $cM->getConnection($connectionKey); } private function mapArticleComment2DomainObject($resultSet) { $comment = new ArticleComment(); $comment->setId($resultSet['ArticleCommentID']); $comment->setName($resultSet['Name']); $comment->setEmail($resultSet['EMail']); $comment->setComment($resultSet['Comment']); $comment->setDate($resultSet['Date']); $comment->setTime($resultSet['Time']); return $comment; } }

Der Quellcode hat dabei folgende Bedeutung:

  • Mit Hilfe von use-Statements werden die benötigten Klassen bekannt gegeben.
  • Die Methode loadArticleCommentByID() läd einen Kommentar an Hand einer ID aus der Datenbank. Es wird hier auf eine Konfiguration für Feldnamen und Datenbank-Tabelle verzichtet, da der commentMapper ohnehin eine konkrete Implementierung eines Data-Mappers für die vorliegende Applikation ist. Die beschleunigt nicht nur das Lade-Verhalten, es ist zudem einfacher zu entwickeln. Die Datebank-Verbindung wird über den ConnectionManager bezogen und das Ergebnis der Abfrage der Methode mapArticleComment2DomainObject() übergeben.
  • mapArticleComment2DomainObject() dient zur Übersetzung des Ergebnisses der Datenbank-Abfrage in das Domänen-Objekt.
  • Die Funktion getConnection() liefert die für das Modul konfigurierte Datenbank-Verbindung zurück.

Um die für dieses Modul notwenige Datenbank-Layout anlegen zu können, muss das im Ordner /APF/modules/comments/data/scripts vorhandene SQL-Script init_comments.sql ausgeführt werden.

Für eigene Anwendungen schickt es sich ebenso, die notwenigen Datenbank-Initialisations-Skripte direkt in einem Ordner des Moduls bzw. der Software abzulegen um später nachvollziehen zu können, welche Anwendung welche Tabellen der Datenbank nutzt.

Die Konfigurationsdatei {ENVIRONMENT}_comments.ini beinhaltet eine Referenz auf eine Datenbank-Verbindung. Letztere findet sich in der globalen Datenbank-Konfiguration im Namespace APF\core\database. Details zur Konfiguration von Datenbank-Verbindungen können dem Kapitel ConnectionManager entnommen werden.

2.4. Business-Schicht

Zur Business-Schicht zählt eine Manager-Klasse, die den Ablauf der Software regelt. Dazu legen wir eine Datei mit dem Namen commentManager.php im Ordner /APF/modules/comments/biz an. Im Fall der Kommentar-Funktion (lesender Zugriff) muss diese lediglich die Daten aus der Datenschicht beziehen und zurück an die Präsentations-Schicht geben.

Wie bereits oben angedeutet verwendet die Kommentar-Funktion den Pager. Aus diesem Grund ist es später notwendig weitere Mechanismen vorzusehen und den Pager entsprechend zu konfigurieren.

Das Grundgerüst der Klasse sieht folgendes vor:

PHP-Code
namespace APF\modules\comments\biz; class ArticleCommentManager extends APFObject { protected $categoryKey; public function init($initParam) { $this->categoryKey = $initParam; } public function loadEntries() { } }

Der Quellcode hat dabei folgende Bedeutung:

  • Die Klassenvariable $categoryKey speichert die Katorgorie, deren Kommentare geladen werden sollen.
  • Um die Business-Komponente mit der Methode getAndInitServiceObject() zur Übergabe der Kategorie aus der Präsentations-Schicht verwenden zu können muss diese eine init()-Methode implementieren.
  • loadEntries() ist der Prototyp der Lade-Methode, die die Einträge an die Präsentations-Schicht zurück gibt.

Kümmern wir uns nun um die Integration des Pager. Dieser soll mit Hilfe des DIServiceManager erzeugt und initialisiert werden. Legen wir hierzu zunächst die Konfigurations-Datei APF\config\modules\comments\{CONTEXT}\{ENVIRONMENT}_serviceobjects.ini an und füllen diese mit folgendem Inhalt:

APF-Konfiguration
[CommentsPager] servicetype = "SINGLETON" class = "APF\modules\pager\biz\PagerManager" conf.entries-per-page.method = "setEntriesPerPage" conf.entries-per-page.value = "10" conf.url-param-page.method = "setPageUrlParameterName" conf.url-param-page.value = "PgrPg" conf.url-param-count.method = "setCountUrlParameterName" conf.url-param-count.value = "PgrAnz" conf.statement-namespace.method = "setStatementNamespace" conf.statement-namespace.value = "APF\modules\comments" conf.count-statement.method = "setCountStatementFile" conf.count-statement.value = "load_entries_count.sql" conf.entries-statement.method = "setEntriesStatementFile" conf.entries-statement.value = "load_entry_ids.sql" conf.statement-params.method = "setStatementParameters" conf.statement-params.value = "CategoryKey:standard" conf.ui-namespace.method = "setPagerUiNamespace" conf.ui-namespace.value = "APF\modules\pager\pres\templates" conf.ui-template.method = "setPagerUiTemplate" conf.ui-template.value = "pager_2" conf.database-connection.method = "setDatabaseConnectionName" conf.database-connection.value = "MySQLi" conf.dynamic-page-size.method = "setAllowDynamicEntriesPerPage" conf.dynamic-page-size.value = "true" conf.caching.method = "setCacheInSession" conf.caching.value = "true"

Details zu den genutzten Parametern können Sie im Kapitel Pager nachlesen.

Um den Pager zu erzeugen bzw. zu verwenden lassen sich folgende Code-Zeilen nutzen:

PHP-Code
$pM = &$this->getDIServiceObject('APF\modules\comments', 'CommentsPager');

Nachdem die Konfiguration für den Pager angelegt ist, können wir die Methode loadEntries() des Managers implementieren:

PHP-Code
public function loadEntries() { $pager = & $this->getPagerManager(); $m = & $this->getServiceObject('APF\modules\comments\data\ArticleCommentMapper'); return $pager->loadEntriesByAppDataComponent($m, 'loadArticleCommentByID', array('CategoryKey' => $this->categoryKey)); }

Im ersten Schritt wird die gewünschte Pager-Instanz bezogen. Im zweiten Schritt wird eine Instanz des Mappers der Applikation erzeugt, der dem Pager dazu dient, die Inhalte direkt aus der Datenbank zu laden.

Alternativ zu dieser Implementierung ist es ebenfalls möglich, den Pager lediglich dazu zu nutzen, die IDs derjenigen Objekte zu laden, die für die aktuelle Seite relevant sind. Das Laden der Inhalte erfolgt dann manuell über die Datenschicht-Komponente der Applikation:

PHP-Code
public function loadEntries() { $pager = & $this->getPagerManager(); $entries = $pager->loadEntries(array('CategoryKey' => $this->categoryKey)); $m = & $this->getServiceObject('APF\modules\comments\data\ArticleCommentMapper'); $entryList = array(); for($i = 0; $i < count($entries); $i++){ $entryList[] = $m->loadArticleCommentByID($entries[$i]); } return $entryList; }

2.5. Präsentations-Schicht

Die Präsentations-Schicht besteht hinsichtlich der Ausgabe lediglich aus einem View: der Liste der Einträge. Zunächst widmen wir uns der Einbindung, da die Kommentar-Funktion für den Entwickler sehr einfach mit einem <core:importdesign />-Tag einbindbar sein soll.

Weiterhin soll der Template-Entwickler eine Möglichkeit erhalten zu bestimmen, welche Kommentare angezeigt werden. Dazu führen wir ein neues Attribut categorykey ein. Dieses definiert die Kategorie derjenigen Kommentaren die angezeigt und eingetragen werden können.

Die Einbindung in ein bestehendes Template kann damit via

APF-Template
<core:importdesign namespace="APF\modules\comments\pres\templates" template="comment" categorykey="****" />

realisiert werden. Das im Tag adressierte Template enthält im wesentlichen einen weiteren <core:importdesign />, der über die Anzeige des Formulars oder der Liste der Einträge entscheidet:

APF-Template
<core:importdesign namespace="APF\modules\comments\pres\templates" template="[coview = listing]" incparam="coview" />

Ist der Parameter coview nicht in der URL vorhanden oder enthält den Wert listing, so wird die Ausgabe der Einträge angezeigt. Für die Ausgabe der Liste dient das Template listing.html:

APF-Template
<@controller class="APF\modules\comments\pres\controller\CommentListingController" @> <div class="cm--list"> <div class="cm--list-head"> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="listing.text.1" /> <a rel="nofollow" href="<html:placeholder name="Link" />#comments" title="<html:getstring namespace="APF\modules\comments" config="language.ini" entry="listing.text.2.title" />"><strong> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="listing.text.2" /> </strong></a> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="listing.text.3" /> </div> <div class="cm--list-pager"> <html:placeholder name="Pager" /> </div> <div class="cm--list-items"> <html:placeholder name="Content" /> </div> </div> <html:template name="ArticleComment"> <div class="cm--list-item"> <div class="cm--list-item-head"> <div class="cm--list-item-head-num"><html:placeholder name="Number" /></div> <div class="cm--list-item-head-date"> <span><html:placeholder name="Name" /></span> <em><html:placeholder name="Date" />, <html:placeholder name="Time" /></em> </div> <div style="clear: left;"></div> </div> <div class="cm--list-item-body"> <html:placeholder name="Comment" /> </div> </div> </html:template> <html:template name="NoEntries"> <div class="cm--list-noentries"> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="noentries.text" /> </div> </html:template>

Im Template findet sich die Definition des zugehörigen Controllers, ein Einleitungs-Text, Platz für die Ausgabe des Pagers und die Ausgabe der Liste. Weiterhin ist ein Templates vorgesehen, das angezeigt wird, wenn keine Einträge vorhanden sind. Die Beschreibung der verwendeten Tags kann unter Standard TagLibs nachgelesen werden.

Die oben beschriebene Einbindung des Templates über das <core:importdesign />-Tag weist im Gegensatz zur Beschreibung des Standard-Tags die Besonderheit auf: die Definition der Kategorie.

Da der Page-Controller jede Template-Datei und jeden Tag als eigenständiges Dokument innerhalb des Objekt-Baums erzeugt, steht auch das Attribut categorykey im entsprechenden DOM-Objekt zur Verfügung. Wie der vorhergehenden Beschreibung der Template-Struktur zu entnehmen ist, wird das Template zur Ausgabe und Anzeige des Formulars nocheinmal im Haupt-Template des Moduls "abstrahiert", sprich als bedingtes Sub-Template eingebunden. Diese Struktur ist im DOM-Baum jeweils durch einen eigenen Knoten abgebildet.

Um nun aus dem Document-Controller heraus auf ein Attribut des Baumes zugreifen zu können, kann zunächst die Referenz auf den DOM-Baum genutzt werden von dem aus eine Navigation möglich ist. Da es sich hierbei um eine Vater-Kind-Beziehung von Template der Listen-Ausgabe zu einbindendem <core:importdesign />-Tag handelt kann das Attribut wie folgt bezogen werden:

PHP-Code
$parent = &$this->getDocument()->getParentObject(); $categoryKey = $parent->getAttribute('categorykey', 'standard');

Da diese Funktion in beiden Document-Controllern (Erzeugen der Liste, Eintragen eines Kommentars) genutzt wird, findet sich diese Funktion in einmn gemeinsamen Basis-Document-Controller wieder. Dieser wird in der Datei /APF/modules/coments/pres/controller/CommentBasecontrollerntroller.php abgelegt und trägt den Namen CommentBasecontroller.

Der konkrete Document-Controller CommentListingController (siehe obiges Template) wird nun mit den Aufgaben betraut, die gewünschten Einträge und den Pager, bzw. ohne Kommentare eine entsprechende Meldung auszugeben. Dieser hat folgenden Inhalt:

PHP-Code
namespace APF\modules\comments\pres\controller; use APF\modules\comments\biz\ArticleComment; use APF\modules\comments\biz\ArticleCommentManager; use APF\tools\link\Url; use APF\tools\string\AdvancedBBCodeParser; use APF\tools\link\LinkGenerator; class CommentListingController extends CommentBaseDocumentController { public function transformContent() { /* @var $m ArticleCommentManager */ $m = &$this->getAndInitServiceObject('APF\modules\comments\biz\ArticleCommentManager', $this->getCategoryKey()); $entries = $m->loadEntries(); $buffer = (string)''; $template = &$this->getTemplate('ArticleComment'); /* @var $bP AdvancedBBCodeParser */ $bP = &$this->getServiceObject('APF\tools\string\AdvancedBBCodeParser'); $bP->removeProvider('standard.font.color'); $bP->removeProvider('standard.font.size'); $i = 1; foreach ($entries as $entry) { /* @var $entry ArticleComment */ $template->setPlaceHolder('Number', $i++); $template->setPlaceHolder('Name', $entry->getName()); $template->setPlaceHolder('Date', \DateTime::createFromFormat('Y-m-d', $entry->getDate())->format('d.m.Y')); $template->setPlaceHolder('Time', $entry->getTime()); $template->setPlaceHolder('Comment', $bP->parseCode($entry->getComment())); $buffer .= $template->transformTemplate(); } if (count($entries) < 1) { $Template__NoEntries = &$this->getTemplate('NoEntries'); $buffer = $Template__NoEntries->transformTemplate(); } $this->setPlaceHolder('Content', $buffer); $this->setPlaceHolder('Pager', $m->getPager('comments')); $urlParams = $m->getURLParameter(); $this->setPlaceHolder('Link', LinkGenerator::generateUrl(Url::fromCurrent()->mergeQuery(array( $urlParams['PageName'] => '', $urlParams['CountName'] => '', 'coview' => 'form' ) ) ) ); } }

Wie der Code-Box entnommen werden kann werden die URL-Paramater des Pagers beim Generieren von Links zurückgesetzt. Dies wird durchgeführt, damit nach dem Erstellen eines Eintrags immer die erste Seite angezeigt wird.

3. Erweiterung der Software

Die Software, wie sie bis Ende des vierten Kapitels beschrieben wurde, könnte nun alle bisher erzeugte Kommentare einer Kategorie auf einer Seite ausgeben, in die das Modul eingebunden ist. Ein Eintragen ist jedoch nicht möglich. Aus diesem Grund soll die Software um das Erstellen von Kommentaren schrittweise erweitert werden. Dazu gehen wir nun in der umgekehrten Reihenfolge vor und beginnen mit der Präsentations-Schicht.

3.1. Präsentations-Schicht

In Kapitel 2.5. haben wir bereits die Möglichkeit vorgesehen einen durch den URL-Parameter coview gesteuerten View einzublenden. Der neu erstellte View für das Formular soll form heißen. Wie das Domänen-Objekt bereits vorgibt sollen vom Benutzer die Eingaben

  • Name
  • E-Mail
  • Kommentar

abgefragt werden. Der Formular-View gestaltet sich wie folgt:

APF-Template
<@controller class="APF\modules\comments\pres\controller\CommentCreateEntryController" @> <div class="cm--create"> <div class="cm--create-head"> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="formhint.text.1" /> <a href="<html:placeholder name="back" />#comments" title="<html:getstring namespace="APF\modules\comments" config="language.ini" entry="formhint.text.2.title" />"><strong><html:getstring namespace="APF\modules\comments" config="language.ini" entry="formhint.text.2" /></strong></a><html:getstring namespace="APF\modules\comments" config="language.ini" entry="formhint.text.3" /> </div> <div class="cm--create-form"> <html:form name="AddComment" method="post"> <span><html:getstring namespace="APF\modules\comments" config="language.ini" entry="form.name" />*</span> <form:text maxlength="100" name="Name" class="cm--create-element-name" /> <br /> <span><html:getstring namespace="APF\modules\comments" config="language.ini" entry="form.email" />*</span> <form:text maxlength="100" name="EMail" class="cm--create-element-email" /> <br /> <br /> <html:getstring namespace="APF\modules\comments" config="language.ini" entry="form.comment" /> <br /> <form:area name="Comment" class="cm--create-element-comment" cols="50" rows="6" /> <br /> <br /> <span><html:getstring namespace="APF\modules\comments" config="language.ini" entry="form.confirm" />*</span> <br /> <br /> <form:captcha text_class="cm--create-element-captcha" name="Captcha" clearonerror="true" /> <br /> <br /> <form:button name="Save" class="cm--create-element-button" /> <form:addfilter class="APF\tools\form\filter\EMailFilter" button="Save" control="EMail" /> <form:addvalidator class="APF\tools\form\validator\TextLengthValidator" button="Save" control="Name|Comment" /> <form:addvalidator class="APF\tools\form\validator\EMailValidator" button="Save" control="EMail" /> <form:addvalidator class="APF\modules\captcha\pres\validator\CaptchaValidator" control="Captcha" button="Save" /> </html:form> </div> </div>

Der zugehörige Document-Controller (CommentCreateEntryController) hat dabei die Aufgabe das Formular entsprechend auszugeben und das Absenden zu kontrollieren. Hat der Benutzer alle Eingaben hinsichtlich der Validierung korrekt getätigt, nutzt er die Business-Komponente zur Speicherung des Eintrags. Dazu implementiert der Manager eine neue Methode saveEntry(), der ein Domain-Objekt übergeben werden muss. Hier der Code des Controllers:

PHP-Code
namespace APF\modules\comments\pres\controller; use APF\modules\comments\biz\ArticleComment; use APF\modules\comments\biz\ArticleCommentManager; use APF\tools\link\LinkGenerator; use APF\tools\link\Url; class CommentCreateEntryController extends CommentBaseDocumentController { public function transformContent() { $form = &$this->getForm('AddComment'); if ($form->isSent() == true) { /* @var $m ArticleCommentManager */ $m = &$this->getAndInitServiceObject('APF\modules\comments\biz\ArticleCommentManager', $this->getCategoryKey()); if ($form->isValid() == true) { $articleComment = new ArticleComment(); $name = &$form->getFormElementByName('Name'); $articleComment->setName($name->getAttribute('value')); $email = &$form->getFormElementByName('EMail'); $articleComment->setEmail($email->getAttribute('value')); $comment = &$form->getFormElementByName('Comment'); $articleComment->setComment($comment->getContent()); $m->saveEntry($articleComment); } else { $this->buildForm(); } } else { $this->buildForm(); } } private function buildForm() { $form = &$this->getForm('AddComment'); $form->setAttribute('action', $_SERVER['REQUEST_URI'] . '#comments'); $config = $this->getConfiguration('APF\modules\comments', 'language.ini'); $button = &$form->getFormElementByName('Save'); $button->setAttribute('value', $config->getSection($this->getLanguage())->getValue('form.button')); $form->transformOnPlace(); $link = LinkGenerator::generateUrl(Url::fromCurrent()->mergeQuery(array('coview' => 'listing'))); $this->setPlaceHolder('back', $link); } }

3.2. Business-Schicht

In diesem Kapitel stellt sich nun die Aufgabe, die zuvor beschriebene Business-Schicht-Methode saveEntry() mit Leben zu füllen. Im Wesentlichen besteht die Aufgabe darin, den neuen Datensatz zu speichern und den Ausgabe-View anzuzeigen. Wie auch beim Laden der Daten muss dazu die Datenschicht-Komponente herangezogen werden. Dieser schreiben wir nun - ohne diese bereits implementiert zu haben - eine Methode saveArticleComment() zu, die wir in der Business-Schicht zur Speicherung des neuen Kommentars nutzen. Die Weiterleitung erledigt ein einfacher Redirect auf den Anzeige-View. Hier muss natürlich darauf geachtet werden, dass die erzeugte Seite auch wieder korrekt angezeigt wird.

PHP-Code
public function saveEntry(ArticleComment $articleComment) { /* @var $M ArticleCommentMapper */ $M = & $this->getServiceObject('APF\modules\comments\data\ArticleCommentMapper'); $articleComment->setCategoryKey($this->categoryKey); $M->saveArticleComment($articleComment); $link = LinkGenerator::generateUrl( Url::fromCurrent() ->mergeQuery(array('coview' => 'listing')) ->setAnchor('comments') ); $this->getResponse()->forward($link); }

Wie Zeile 5 ($articleComment->setCategoryKey(...)) zeigt, manipuliert die Business-Schicht das Domain-Objekt, damit dieses mit der korrekten Kategorie gespeichert wird. Anschließend wird - wie bereits für die Generierung des Links für das Formular - der LinkGenerator::generateUrl() verwendet.

3.3. Datenschicht

Die Datenschicht muss nun noch die Methode saveArticleComment() implementieren:

PHP-Code
public function saveArticleComment(ArticleComment $comment) { $conn = & $this->getConnection(); if ($comment->getId() == null) { $insert = 'INSERT INTO article_comments (Name, EMail, Comment, Date, Time, CategoryKey) VALUES (\'' . $conn->escapeValue($comment->getName()) . '\',\'' . $conn->escapeValue($comment->getEmail()) . '\',\'' . $conn->escapeValue($comment->getComment()) . '\',CURDATE(),CURTIME(),\'' . $comment->getCategoryKey() . '\');'; $conn->executeTextStatement($insert); } }

Die Bedeutung der Code-Zeilen lässt sich leicht erschließen: zunächst wird eine Instanz der jeweils konfigurierten Datenbank-Connection über den ConnectionManager bezogen. Anschließend werden die Daten nach einer Prüfung in in eine flache, relationale Strukur zerlegt und per SQL-Statament gespeichert.

4. Ausblick

Im Anschluss an das aktuelle Tutorial empfiehlt sich der Artikel Objektorientiertes Design eines Gästebuchs als weiterführende Lektüre.

Hinsichtlich der Implementierung sei an dieser Stelle nochmals darauf hinweisen, dass die Methoden getServiceObject() und getAndInitServiceObject() des ServiceManager die jeweils verwendeten Komponeten mit den notwendigen Informationen (z.B. Sprache, Context, ...) versorgen. Es ist daher für die Nutzung von Service-Komponenten stets ratsam, den ServiceManager zur Erzeugung zu nutzen um durchgängig Zugriff auf die genannten Eigenschaften zu haben.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.