Adventure,PHP,Framework,PageController,FrontController,Pattern,Objektorientierung,OO,Software,Design,Wiederverwendbarkeit,UML,Tutorial,Benchmark,ausgezeichnete Performance

Suche:    
Downloads  |  SVN!  |  Roadmap  |  Forum!  |  Bugtracking  |  Gästebuch  |  Backlinks!  |  Referenzen!  |  Sitemap  |  Impressum  
 
Deutsch | English Adventure PHP Framework  Bookmark @ Technorati Bookmark @ del.icio.us Bookmark @ Mr. Wong Bookmark @ Simpy Bookmark @ Google Bookmark @ Digg.com Adventure PHP Framework Seite 032-Kommentarfunktion-Tutorial drucken!

Kommentar-Funktion

Artikel bewerten:
Dieser Artikel wurde noch nicht bewertet. Bewerten Sie diesen Artikel als erstes!

1. Einleitung

Das Tutorial soll zeigen, wie das Kommentar-Modul am Ende der Dokumentations-Seiten aufgebaut ist und welche Komponenten zur Funktionsweise beitragen. Es soll nochmals verdeutlicht werden, welche Richtlinen die im Manual genannten Design-Pattern wirklich geben und welche Hintergründe diese haben. Aus diesem Grund soll das Tutorial wie folgt aufgebaut sein:
  • Beschreibung des Aufbaus der Applikation,
  • Verwendung von Modulen zur Unterstützung und
  • Erläuterung der Programm-Dateien.
Dabei werden die unter Grundlagen bereits vermittelten Design-Pattern praktisch für diesen Anwendungsfall aufbereitet. Dieses Tutorial sollte deshalb als Einstiegs-Tutorial vor den beiden Tutorials Kontaktformular und Gästebuch zum Studium herangezogen werden.



2. Aufbau der Applikation

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. Folgt man der Lehre des objektorientierten Applikations-Entwurfs, so gibt es dort eine Vielzahl an Entwurfsmuster (Pattern), die zum Entwurf der Kommentar-Funktion herangezogen werden können. Die in diesem Entwurf eingeschlossenen Muster und deren Auswirkungen seien hier erläutert:


2.1. 3-Schicht-Architektur

Das Pattern der "3-Schicht-Architektur" geht davon aus, dass es von Vorteil ist, ein Programm - hier ein Teil einer Gesamt-Applikation - in die Schichten Daten-Schicht, Business-Schicht und Präsentations-Schicht zu unterteilen, da so eine größere Transparenz des Programm-Codes garantiert werden kann. Dies erzeugt im ersten Wurf zwar einen Mehraufwand, der sich jedoch mit dem Betrieb und der Weiterentwicklung der Software auszahlt. Ein weiterer Vorteil ist, dass mit Einführung des Schichten-Modells beliebige Schichten einfach ausgetauscht werden können, ohne dass die Funktion der verbleibenen Schichten geändert werden muss. Für Kommentar-Funktion gilt dann:
  • Daten-Schicht:
    Liest die Daten aus der Datenbank aus und stellt sie der nächst höheren Schicht, bzw. nimmt Daten entgegen und speichert sie in der Datenbank.
  • Business-Schicht:
    Sie kontrolliert die Funktion der Software, und steuert die Ein- und Ausgabe und den Ablauf der Software.
  • Präsentations-Schicht:
    Die Präsentations-Schicht kümmert sich um die Präsentation der Applikation zum Kunden. Hierzu zählt die Ausgabe einer GUI (im Web-Umfeld HTML) und die damit verbundenen Funktionen, jedoch nicht mehr.
Im objektorientierten Anwendungsdesign hat sich zu diesen Bereichen in der Vergangenheit ein einiger Sprachgebrauch "eingebürgert", der auch hier verwendet werden soll. So wird bei der Datenschicht- Komponente von einem Mapper gesprochen (siehe 2.2), bei der Business-Schicht von einem Manager und in der Präsentations-Schicht je nach Art dieser von View- bzw. hier von DocumentControllern gesprochen. Für diese Begriffe existiert in der Regel eine jeweils eigene Klasse. In der Präsentations-Schicht kann dies jedoch je nach Anzahl der Views und Anzeige-Module einer Applikation variieren. Im Fall der Kommentar-Funktion wird die Software zwei Views mit jeweils einem Template und einem Controller besitzen. Dieser Verfahrensweise begegnen Sie auch in den folgenden Tutorials.


2.2. (OR-)Data-Mapper

Ein Data-Mapper ist ein Vermittler zwischen der "Welt" der Datenbank und der "Welt" der Anwendung. Das bedeutet, dass diese Komponente - wie bereits oben angedeutet - sich mit den Spezifika der Datenbank beschäftigt und der Software eine einfache Schnittstelle bietet um auf dieser Daten zu speichern und anschließend wieder auszulesen. Konkret wird der Mapper - eine Komponente der Datenschicht - die zur Darstellung benöätigten Daten auslesen und im Format der Anwendung bereitstellen, bzw. umgekehrt, einen neuen Kommentar speichern. Das Präfix OR erhält der Mapper in diesem Fall deshalb, weil eine relationale Datenbank zur Speicherung der Daten verwendet wird (MySQL) und die Anwendung jedoch objektorientiert entworfen wird. Eine klassische damit verbundene Aufgabe des OR-Mappers ist es ein Ergebnis-Array in ein Objekt der Anwendung (Domain-Object) zu mappen - zu übersetzen. Üblicherweise implementiert der Mapper dazu eine Methode map{ObjectName}2DomainObject(), wobei {ObjectName} jeweils durch den Namen des Domain-Objekts bzw. der Domain-Objekte (siehe 2.3) ersetzt wird. Als Parameter trägt die Funktion ein Array und der Rückgabe-Wert ist immer vom Typ Object {ObjectName} - das Domänen-Objekt. Darüber hinaus besitzt der Mapper üblicherweise Methoden zum Lesen von Objekten per ID, bzw. per in der Anwendung benötigten Schlüsseln und zum Schreiben dieser.


2.3. Domain-Object-Pattern

Das Domain-Object-Pattern besagt vereinfacht gesürochen, dass eine Anwendung immer nur den Teil der in der Datenhaltung vorhandenen Objekte nutzen sollte, die für die Applikation auch bestimmt ist. In einem großen Datenhaltungs-Konzept werden in der Regel erheblich mehr Daten abgespeichert, als eine einzelne Anwendung benötigt. Nehmen wir an, in der für die Kommentar-Funktion genutzen Datenbank werden neben Kommentaren noch Termine, Orte, Länder und deren Beziehungen gespeichert, dann benötigt die Kommentar-Funktion nur den (kleinen) Teil der Kommentare aus diesen Daten-Topf und es wäre überflüssig, wenn sich die Anwendung auch mit den übrigen Objekten beschäftigen würde. Aus diesem Grund beschäftigt sich jede Domäne - hier Kommentar-Funktion - nur mit den Domänen-Objekten ihres Interesses. Die gesammte Applikation "kennt" damit ausschließlich diese Menge von Objekten und kann mit diesen umgehen. Domain-Objekte sind üblicherweise Objekte der Business-Schicht und werden auch in dem dafür vorgesehenen Ordner abgelegt. Wichtig ist hier jedoch, dass alle Schichten diese Objekte "kennen". Die Datenschicht "schneidet" diese aus dem großen Pool an Daten "heraus" um später den Extrakt wieder "einpflanzen" zu können, die Business-Schicht bildet an Hand der Objekte ihren internen Ablauf ab und die Präsentations-Schicht nutzt diese zur Darstellung der Benutzer-GUI nutzt.


2.4. MVC

Das Model-View-Controller-Pattern ist ein Entwurfsmuster der Präsentations-Schicht und ist im Sinne der 3-Schicht-Architektur lediglich eine "Verfeinerung" des Präsentations- Schicht-Designs. Es beschreibt in Zusammenarbeit mit der 3-Schicht-Architektur - wie auch bereits unter Grundlagen aufgeführt wurde - die Trennung zwischen dem dargestellten Inhalt und der Definition der Applikations-Workflows (Model), der Beschreibung der Darstellung und des Aussehens der Applikation (View) und der eigentlichen Darstellungs-Logik (Controller). Genau wie das 3-Schicht-Architektur-Pattern verspricht sich der Entwickler auch hier mehr Flexibilität bei Erweiterung und Wartung der Applikation.

Im Fall der Kommentar-Funktion wird eine gemeinsame Business-Komponente (Model) verwendet, die aus dem Manager und dem Domain-Objekt besteht. In diesen sind zum einen der Ablauf der Applikation und zum anderen die Daten zur Ausgabe gespeichert. Zur weiteren Strukturierung der Präsentations-Schicht werden zwei Unterordner angelegt, die später die Controller- und die View-Dateien aufnehmen. So kann auf einen Blick bereits beurteilt werden, zu welchem Programmteil welche Datei gehört.

MVC ist jedoch zunächst ein Pattern, das weitere Tools benötigt um dem Entwickler eine sinnvolle Möglichkeit zu bieten Anwendungen zu verfassen ohne mit jeder Anwendung das Rad neu erfinden zu müssen. Aus diesem Grund wurdem im Adventure-PHP-Framework weitere Pattern zum Design der Präsentations-Schicht und zur Unterstützung des Entwicklers herangezogen.


2.5. PageController

Der PageController ist in Zusammenarbeit mit dem Composite-Pattern ein generisches Hilfmittel für den Anwender, MVC-Applikationen zu entwickeln ohne den dazu notwendigen Rahmen bei jeder Applikation neu erstellen zu müssen. Zudem bietet der PageController einen Mechanismus, wie der Entwickler Module auf GUI-Ebene in eine bereits bestehende Applikation einklinken kann, die für die Kommentar-Funktion von Wichtigkeit ist, da diese "einfach per Tag" in eine bereits bestehende Artikel-Seite eingebunden wird.



3. Entwurf der Software

Im aktuellen Turorial soll auf einen detaillierten Entwurf der Software via UML verzichtet werden, denn eine Kommentar-Funktion besitzt keine so hohe Komplexität, dass ein schriftliches Design von Nöten wäre. Zudem erreicht man mit der unter Kapitel 2 erläuterten Struktur bereits ein sehr generisches Anwendungsdesign, das sehr einfach erweitert werden kann.



4. Implementierung der Software

Für das Vorgehen bei der Implementierung der Software kann der Autor kein allgemeingültiges Rezept ausstellen. Oft schickt es sich eine Applikation von oben nach unten, oft umgekehrt zu entwickeln, in manchen Fällen zunächst den rein lesenden und anschließend erst den schreibenden Teil zu erstellen. In diesem Fall zieht der Autor letztere vor um schnell ein anzeigbares Ergebnis zu erhalten.


4.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, handelt es sich um ein Modul, das im Ordner /apps/modules abgelegt wird. Dem Modul wird nun der Name comments gegeben, was gleichzeitig auch der Ordner-Name sein muss. Für die oben genannten Schichten werden jeweils eigene Ordner (data, biz, pres) angelegt und im Ordner pres nochmals weitere Unterordner für die Controller (documentcontroller) und die Views (templates). Es ergibt sich somit eine Ordnerstruktur von
/apps
     /config
     /core
     /modules
             /comments
                      /biz
                      /data
                      /pres
                           /documentcontroller
                           /templates
     /sites
     /tools
Da die Business-Schicht die Pager-Komponente nutzt müssen später weitere Strukturen unter config angelegt werden, was aber zu gegebener Zeit erklärt wird.


4.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 /apps/modules/comments/biz erstellt un mit folgenden Inhalt gefüllt:
class ArticleComment extends coreObject
{

   var 
$__ID null;
   var 
$__Name;
   var 
$__EMail;
   var 
$__Comment;
   var 
$__Date;
   var 
$__Time;
   var 
$__CategoryKey;

   function 
ArticleComment(){
   }

 
// end class
Da die Klasse von coreObject erbt müssen keine get()- bzw. set()-Methoden implementiert werden, da die Klasse coreObject bereits über abstrakte Methoden für das Lesen und Setzen von privaten Klassen-Variablen besitzt, die später auch dafür genutzt werden können. Um diese jedoch nutzen zu können muss die Namens-Konvention eingehalten werden, die besagt, dass jede private und durch die Methoden get()- bzw. set() manipulierten Klassen-Variablen mit $__{Name} benannt sein müssen, wobei {Name} durch den jeweils erwünschten Namen ersetzt werden muss. Anschließend kann per
   $AC = new ArticleComment();
   
$AC->set('Name','Max Mustermann');
   echo 
$AC->get('Name'); 
die Ausgabe
  Max Mustermann
erzeugt werden.


4.3. Datenschicht

Die Datenschicht besteht gemäß den Ausführungen unter Kapitel 2 als einem DataMapper, der zunächst nur eine Lese-Funktion erhält. An dieser Stelle muss vorweggenommen werden, dass innerhalb der Anwednung die Komponente "Pager" zum Einsatz kommt. Diese läd an Hand von URL-Parametern IDs der gewünschten Datensätze aus der Datenbank und erwartet, dass der Data-Mapper lediglich eine Funktion bereitstellt, die an Hand von IDs Domänen-Objekte läd. Dazu erstellen wir eine Datei mit dem Namen der Klasse - plus Endung - (commentMapper.php) unter /apps/modules/comments/data. Diese trägt zunächst folgenden Inhalt:
   import('modules::comments::biz','ArticleComment');
   
import('core::database','MySQLHandler');


   class 
commentMapper extends coreObject
   
{

      function 
commentMapper(){
      }


      function 
loadArticleCommentByID($ArticleCommentID){

         
// SQL-Handler holen
         
$SQL = &$this->__getServiceObject('core::database','MySQLHandler');

         
// Eintrag selektieren
         
$select 'SELECT ArticleCommentID, Name, EMail, Comment, Date, Time
                    FROM article_comments
                    WHERE ArticleCommentID = \''
.$ArticleCommentID.'\';';
         
$result $SQL->executeTextStatement($select);

         
// Objekt zurückgeben
         
return $this->__mapArticleComment2DomainObject($SQL->fetchData($result));

       
// end function
      
}


      function 
__mapArticleComment2DomainObject($ResultSet){

         
// Neues Objekt erstellen
         
$ArticleComment = new ArticleComment();

         
// ArticleCommentID
         
if(isset($ResultSet['ArticleCommentID'])){
            
$ArticleComment->set('ID',$ResultSet['ArticleCommentID']);
          
// end if
         
}

         
// Name
         
if(isset($ResultSet['Name'])){
            
$ArticleComment->set('Name',$ResultSet['Name']);
          
// end if
         
}

         
// EMail
         
if(isset($ResultSet['EMail'])){
            
$ArticleComment->set('EMail',$ResultSet['EMail']);
          
// end if
         
}

         
// Comment
         
if(isset($ResultSet['Comment'])){
            
$ArticleComment->set('Comment',$ResultSet['Comment']);
          
// end if
         
}

         
// Date
         
if(isset($ResultSet['Date'])){
            
$ArticleComment->set('Date',$ResultSet['Date']);
          
// end if
         
}

         
// Time
         
if(isset($ResultSet['Time'])){
            
$ArticleComment->set('Time',$ResultSet['Time']);
          
// end if
         
}

         
// Gefülltes Objekt zurückgeben
         
return $ArticleComment;

       
// end function
      
}

    
// end class
   
Der Quellcode hat dabei folgende Bedeutung:
  • Die beiden import()-Befehle binden die benötigten Klassen ein. Hierzu gehört das Domain-Objekt ArticleComment und die MySQL-Schnittstellen-Klasse MySQLHandler.
  • 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 DataMappers für die vorliegende Applikation ist. Die beschleunigt nicht nur das Lade-Verhalten, es ist zudem einfacher zu entwickeln. Innerhalb dieser Methode wird vom MySQL-Wrapper gebrauch gemacht, über den eine Result gezogen wird. Anschließend wird das Ergebnis abgeholt und der privaten Methode __mapArticleComment2DomainObject() übergeben und ds Ergebnis an den Aufrufenden zurückgegeben.
  • __mapArticleComment2DomainObject() ist die unter Kapitel 2 angesprochene Implementierung einer Mapper-Methode, die ein Array in ein Domain-Objekt mappt.
Um die für dieses Modul notwenige Datenbank-Layout anlegen zu können, muss das im Ordner /apps/modules/comments/data/scripts vorhandene SQL-Script init_comments.sql ausgeführt werden. Für weitere, von Ihnen erstellte Anwendungen schickt es sich ebenso, die notwenigen Datenbank-Initialisations-Skripte direkt in einem Ordner der Datenschicht abzulegen um später nachvollziehen zu können, welche Anwendung, welche Tabellen der Datenbank nutzt.


4.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 /apps/modules/comments/biz an. Im Fall der Kommentar-Funktion (lesender Zugriff) muss diese lediglich die Daten aus der Datenschicht beziehen und diese zurück an die Präsentations-Schicht geben. Wie bereits oben angedeutet verwendet die Kommentar-Funktion den Pager. Aus diesem Grund ist es notwendig weitere Mechanismen vorzusehen und den Pager entsprechend zu konfigurieren. Das Grundgerüst der Klasse sieht jedoch wie folgt aus:
   import('modules::pager::biz','pagerManager');
   
import('modules::comments::data','commentMapper');
   
import('modules::pager::biz','pagerManager');
   
import('tools::link','frontcontrollerLinkHandler');


   class 
commentManager extends coreObject
   
{

      var 
$__CategoryKey;


      function 
commentManager(){
      }


      function 
init($CategoryKey){
         
$this->__CategoryKey $CategoryKey;
       
// end function
      
}


      function 
loadEntries(){
      }

    
// end class
   
Der Quellcode hat dabei folgende Bedeutung:
  • Die import()-Befehle binden die benötigten Klassen ein. Hierzu gehört das Domain-Objekt ArticleComment, die Datenschicht-Komponente commentMapper, der PagerManager und der frontcontrollerLinkHandler, der später zur Generierung eines Links für die Weiterleitung während des Speicher-Vorgangs genutzt wird.
  • Die Klassenvariable $__CategoryKey speichert die Katorgorie, dessen Kommentare geladen werden sollen.
  • Um die Business-Komponente mit der Methode getAndInitServiceObject() verwenden zu können muss diese eine init()-Methode implementieren, die die Klasse initialisiert. In diesem Fall soll die Klasse mit der entsprechenden Kommentar-Kategorie bestückt werden.
  • loadEntries() ist der Prototyp der Lade-Methode, die die Einträge an die Präsentations-Schicht zurückgibt.
Kümmern wir uns nun um die zuletzt genannte Funktion. Die Komponente pagerManager hat gemäß API-Dokumentation die Methoden
  • setAnchorName() (Name des Ankers setzen)
  • loadEntries() (IDs von Einträgen laden)
  • getPager() (HTML-Ausgabe des Pagers erzeugen)
  • getPagerURLParameters() (Ausgabe der URL-Parameter der aktuellen Pager-Konfiguration)
und eine Referenz auf einen Pager wird mit Hilfe der pagerManagerFabric bezogen. Der Fabric muss dazu der Abschnitt der zu verwendenden Konfiguration und ein Initialisierungs-Parameter mitgegeben werden. In PHP-Code funktioniert das wie folgt:
   // Beziehen einer Singleton-Instanz der Fabric
   
$pMF = &$this->__getServiceObject('modules::pager::biz','pagerManagerFabric');

   
// Beziehen und initialisieren des PagerManagers
   
$pM = &$pMF->getPagerManager('ArticleComments',array('CategoryKey' => $this->__CategoryKey)); 
Damit wird ein PagerManager erzeugt, die Konfiguration aus dem Abschnitt ArticleComments geladen und dieser mit dem Parameter CategoryKey und dem Wert aus der lokalen Klassen-Variable $__CategoryKey gespeist. Das zweite Argument der Methode getPagerManager() initialisiert die zusätzlichen SQL-Statement-Parameter, die einem ID- oder Count-Statement mitgegeben werden können.

Die Konfiguration des pagerManager's ist unter dessen Namespace (modules::pager) und dem aktuellen Context der Applikation abgelegt. Im Fall der Dokumentationsseite ist das sites::demosite. Daraus ergibt sich der Ordner-Pfad /apps/config/modules/pager/sites/demosite. Dort befindet sich eine Datei mit dem Namen DEFAULT_pager.ini mit ungefähr folgendem Inhalt:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ArticleComments                                                                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[ArticleComments]
; Anzahl Einträge pro Seite
Pager.EntriesPerPage = "5"

; Namen der URI-Parameter für Startpunkt und Anzahl/Seite
Pager.ParameterStartName = "PgrStr"
Pager.ParameterCountName = "PgrAnz"

; Namespace und Statements für Selektionen inkl. Konfiguration der Statements
Pager.StatementNamespace = "modules::comments"
Pager.CountStatement = "load_entries_count"
Pager.CountStatement.Params = "CategoryKey:standard"
Pager.EntriesStatement = "load_entry_ids"
Pager.EntriesStatement.Params = "CategoryKey:standard"

; Pager-Ausgabe Design
Pager.DesignNamespace = "modules::pager::pres::templates"
Pager.DesignTemplate = "pager_2"
Mit diesem Satz von Parametern kann der Pager vollständig konfiguriert werden. Einge davon können für die meißten Anwendungen ohne Änderung belassen werden. Was jedoch von Anwendungsfall zu Anwendungsfall variiert sind die Definition der Statements. Der Pager verlangt - um seine Arbeit verrichten zu können - nach zwei SQL-Statements, die er zum Laden der Anzahl der in der Datenbank befindlichen Datenstätze und zum Laden der für eine Seite relevanten IDs verwendet. Diese Statements werden durch ihren Namespace und durch ihren Namen bestimmt, wobei der Namespace der Ordner-Pfad - getrennt durch "::" statt "/" - nur demjenigen Pfad entspricht, der den Context der Applikation nicht enthält. Der Context wird dem Pager dadurch übergeben, dass dieser mit der PagerManagerFabric erzeugt wird. Die Statements-Dateien befinden sich im aktuellen Anwendungsfall in einem Unterordner des Moduls "comments", da die Statements immer unter dem zugehörigen Modul-Namespace abgelegt werden, nicht unter dem Pager-Namespace. Übersetzt man die oben konfigurierten Ordner-Pfade, müssen die Statement-Dateien unter dem Ordner /apps/config/modules/comments/sites/demosite/statements liegent. Der Ordner statements wird vom Datenbank-Wrapper MySQLHandler verlangt. Da Statements einer Konfiguration gleich kommen werden diese nicht nur unter dem config-Namespace abgelegt, sondern auch so benannt. Wie unter Konfiguration beschrieben, muss dem Dateinamen, der Wert der Umgebungsvariable vorangestellt und eine Endung angehängt werden. Im Fall der SQL-Statements ist die Endung .sql. Die Dateien müssen deshalb
  • DEFAULT_load_entries_count.sql
  • DEFAULT_load_entry_ids.sql
lauten. Der PagerManager erwartet eine weitere Konvention: die Benennung der Rückgabe-Variablen und das Einfügen von Parametern. Am Beispiel des Entries-Count-Statement lässt sich das einfach zeigen:
SELECT COUNT(*) AS EntriesCount
FROM article_comments
WHERE CategoryKey = '[CategoryKey]'
GROUP BY ArticleCommentID;
Der PagerManager erwartet intern immer ein Ergebnis in der Variable EntriesCount, weswegen das Ergebnis des Select-Statements auch in diesen Alias geschrieben werden muss. Weitere, der PagerManagerFaric zuvor übergebene Parameter können im Statement mit eckigen Klammern eingeschlossen verwendet werden. Für das Load-Statement gelten folgende Regeln:
SELECT ArticleCommentID AS DB_ID
FROM article_comments
WHERE CategoryKey = '[CategoryKey]'
ORDER BY Date DESC, Time DESC
LIMIT [Start],[EntriesCount];
Auch hier erwartet der PagerManager die Ergebnis-IDs in der Variable DB_ID - ein Alias ist notwenig. Weitere Statement-Parameter können einfach wie oben beschrieben verwendet werden. Eine weitere Besonderheit ist die LIMIT-Anweisung. Diese muss die Parameter
  • [Start]
  • [EntriesCount]
enthalten, da diese mit internen Pager-Variablen gefüllt werden um jeweils nur diejenigen Einträge auszugeben, die für die aktuelle Seite vorgesehen sind. Die restlichen Konstrukte der Statements können beliebig komplex gewählt werden, solange die genannten Struktur-Elemente eingehalten werden.

Kommen wir nun nach der Konfiguration des Pagers zu dessen Verwendung. Die Methode loadEntries() des Managers gestaltet sich damit wie folgt:
   function loadEntries(){

      
// pagerManager holen
      
$pMF = &$this->__getServiceObject('modules::pager::biz','pagerManagerFabric');
      
$pM = &$pMF->getPagerManager('ArticleComments',array('CategoryKey' => $this->__CategoryKey));

      
// Kommentare laden
      
$M = &$this->__getServiceObject('modules::comments::data','commentMapper');
      return 
$pM->loadEntriesByAppDataComponent($M,'loadArticleCommentByID');

    
// end function
   
Im ersten Schritt wird die gewünschte Pager-Instanz über die PagerManagerFabric geholt, im zweiten Schritt wird eine Instanz des DataMappers der Applikation erzeugt. Diese wird dann in darauf folgenden Schritt als Data-Provider dem PagerManager übergeben, damit dieser die gewünschten Domänen-Objekte läd. Eine weitere Möglichkeit den Pager anzuwenden ist sich zunächst die IDs der auf der aktuell anzuzeigenden Seite zu ziehen und dann im Manager die Objekte per Schleife mit Hilfe des Mappers zu laden. Das sieht in PHP-Code wie folgt aus:
   function loadEntries(){

      
// pagerManager holen
      
$pMF = &$this->__getServiceObject('modules::pager::biz','pagerManagerFabric');
      
$pM = &$pMF->getPagerManager('ArticleComments',array('CategoryKey' => $this->__CategoryKey));

      
// Ids laden
      
$EntryIDs $pM->loadEntries();

      
// Kommentare laden
      
$M = &$this->__getServiceObject('modules::comments::data','commentMapper');

      
$Entries = array();

      for(
$i 0$i count($EntryIDs); $i++){
         
$Entries[] = $M->loadArticleCommentByID($EntryIDs[$i]);
       
// end for
      
}

      
// Liste zurückgeben
      
return $Entries;

    
// end function
   
Beide Möglichkeiten werden angeboten, letztere ist für denjenigen Anwendungsfall gedacht, bei dem die Business-Komponente noch Einfluss auf die Sortierung oder den Inhalt der Domain-Objects haben möchte oder muss. Innerhalb der Kommentar-Funktion wird die zu erst genannte Variante eingesetzt in den folgenden Tutorials die zuletzt genannte. Damit wäre der Teil der lesenden Anwendung für die Business-Schicht abgeschlossen.


4.5. Präsentations-Schicht

Die Ausgabe der Kommentare in einer blätterbaren Seite beinhaltet neben der Einbindung des Moduls lediglich einen View - die Ausgabe-Liste. Widmen wir uns zunächst der Einbindung, da diese mit einer Besonderheit ausgestattet werden soll. Wie bereits weiter oben angesprochen, soll es möglich sein, die Kommentar-Funktion in eine bestehende Seite per XML-Tag einbindbar zu machen. Dazu nutzen wir in diesem Anwendungsfall das <core:importdesign />-Tag. Dieses trägt laut Standard-TagLibs die XML-Parameter namespace um den Namespace des Templates zu deklarieren, template für die Benennung des Templates und den optionalen Parameter incparam um den URL-Parameter, der die Einbindung steuert zu benennen. Letzterer soll auch hier zum Einsatz kommen, da der Standard-Parameter "pagepart" unter Umständen bereits in Gebrauch sein könnte und die Ausführung der Funktion stören könnte. Weiterhin soll der Template-Bauer, der die Funktion einbaut entscheiden können, welche Kommentare angezeigt werden. Dazu führen wir einen neuen Parameter categorykey ein, der beschreiben soll, welche Kategorie von Kommentaren hier eingetragen werden soll und angezeigt werden kann. Dieser Mechanismus wurde oben bereits ohne Kommentar vorgesehen, ist aber - mit dem Wissen der hier verfassten Zeilen - notwenig um eine Unterscheidung der Kommentare vorzusehen. Datenbank-technisch ist sicher auch eine andere Möglichkeit der Kategorisierung denkbar, es handelt sich jedoch um eine einfaches Beispiel, bei dem von einem komplexen Datenbank-Design abgesehen wird.

Die Einbindung in ein bestehendes Template kann damit mit einem
  <core:importdesign
      namespace="modules::comments::pres::templates"
      template="comment"
      categorykey="****"
  />
erledigt werden. Im dort genannten Template wird nun der Kopf des Moduls durch
  <a name="comments" /><h2>Kommentare</h2>
beschrieben und mit
  <core:importdesign
      namespace="modules::comments::pres::templates"
      template="[coview = listing]"
      incparam="coview"
  />
anschließend der gerade angefragte View (Liste oder Formular) an Hand des URL-Parameters coview (coview für CommentView) eingebunden. Für die Ausgabe der Liste soll nun Template listing erstellt werden:
<@controller namespace="modules::comments::pres::documentcontroller" file="comment_listing_v1_controller" class="comment_listing_v1_controller" @>
<core:addtaglib namespace="tools::html::taglib" prefix="html" class="getstring" />
<html:getstring namespace="modules::comments" config="language" entry="listing.text.1" /> <a href="<html:placeholder name="Link" />#comments" title="<html:getstring namespace="modules::comments" config="language" entry="listing.text.2.title" />"><strong><html:getstring namespace="modules::comments" config="language" entry="listing.text.2" /></strong></a> <html:getstring namespace="modules::comments" config="language" entry="listing.text.3" />
<br />
<br />
<html:placeholder name="Pager" />
<html:placeholder name="Content" />

<html:template name="ArticleComment">
  <table cellspacing="0" cellpadding="0" style="border: 0px solid black; width: 100%;">
    <tr>
      <td style="width: 60px; text-align: left; padding: 2px;">
        <font style="font-size: 36px; color: #777BB4; font-style: italic;">
          <template:placeholder name="Number" />
        </font>
      </td>
      <td style="padding: 2px; font-size: 12px; font-family: Arial, Helvetica, sans-serif;">
        <strong><template:placeholder name="Name" /></strong>
        <br />
        <em><template:placeholder name="Date" />, <template:placeholder name="Time" /></em>
      </td>
    </tr>
  </table>
  <div style="background-color: #eeeeee; width: 100%; padding: 2px;">
    <template:placeholder name="Comment" />
  </div>
  <br />
</html:template>

<html:template name="NoEntries">
  <template:addtaglib namespace="tools::html::taglib" prefix="template" class="getstring" />
  <br />
  <span style="color: #777BB4; font-style: italic; margin-left: 30px; font-weight: bold;"><template:getstring namespace="modules::comments" config="language" entry="noentries.text" /></span>
  <br />
  <br />
  <br />
</html:template>

<html:template name="Deactivated">
  <template:addtaglib namespace="tools::html::taglib" prefix="template" class="getstring" />
  <br />
  <span style="color: #777BB4; font-style: italic; margin-left: 30px; font-weight: bold;"><template:getstring namespace="modules::comments" config="language" entry="deactivated.text" /></span>
  <br />
  <br />
  <br />
</html:template>
Dieses sieht die Definition des zu verwendenden Controllers, einen Einleitungstext, Platz für die Ausgabe eines Pagers und die Ausgabe der Liste und ein Templates für einen Eintrag und die Anzeige der Meldung, dass keine Einträge vorhanden sind. Die Beschreibung der verwendeten Tags kann unter Standard-TagLibs nachgelesen werden. Interessanter ist nun die Gestaltung des DocumentControllers für die Ausgabe der Liste.

An dieser Stelle soll jedoch zunächst ein kleiner Exkurs in die Struktur des GUI-Designs des Adventure-PHP-Frameworks eingeschoben werden um die Funktion der Tags am Beispiel zu erläutern.
Der PageController erzeugt aus jedem Template einen eigenen DOM-Knoten im Objektbaum der Oberfläche. Durch Referenzen eines Knotens auf seinen Vater-Knoten können ausgehend von einem beliebigen DOM-Objekt die Eigenschaften des Vater- und/oder Kind-Objekts ausgelesen werden. Diese Eigenschaft machen wir uns bei der Ausgabe der Liste und später dem Eintragen eines Kommentars zu Nutze. Die aktuelle Struktur sieht vor, dass in einem beliebigen Template die Kommentar-Funktion durch ein parametrisiertes <core:importdesign />-Tag eingebunden wird. Dieses wiederum bindet ein weiteres Template ein, das für Ausgabe der Liste oder Anzeige des Menüs zuständig ist. Letztere benötigen jedoch Zugriff auf das im "Haupt-Template" gesetzte Tag-Attribut um die richtigen Kommentare ausgeben, oder in die richtige Kategorie eintragen zu können. Gemäß dem DOM-Modell ist das innerhalb eines DocumentControllers, der wiederum eine Referenz auf das ihm zugewiesene Document-Objekt besitzt, durch den folgenden PHP-Code möglich:
   $DocParent = &$this->__Document->getByReference('ParentObject');
   
$this->__CategoryKey $DocParent->getAttribute('categorykey'); 
Um bei fehlenden Attribut keine Fehlermeldung zu bekommen wird der Code noch um
   if($CategoryKey == null){
      
$this->__CategoryKey 'standard';
   
// end if
   
}
   else{
      
$this->__CategoryKey $CategoryKey;
   
// end else
   
ergänzt. Da diese Funktion in beiden DocumentControllern wichtig ist (Erzeugen der Liste, Eintragen eines Kommentars) wird diese Funktion in einen gemeinsamen Basis-DocumentController verpackt. Dieser wird mit commentBaseController benannt und unter /apps/modules/coments/pres/documentcontroller abgelegt. Der abstrakte Controller wird zudem für die Einbindung des Domain-Objekts und des Managers verwendet. Alle konkreten Controller erhalten diese Funktion nun automatisch dadurch, dass diese vom commentBaseController erben.

Der konkrete DocumentController comment_listing_v1_controller (siehe Template) wird nun mit den Aufgaben betraut, die gewünschten Einträge und den Pager, bzw. ohne Kommentare eine Meldung auszugeben. In Quellcode gegossen kann das so gelöst werden:
import('modules::comments::pres::documentcontroller','commentBaseController');
import('tools::datetime','dateTimeManager');
import('tools::string','bbCodeParser');
import('tools::link','frontcontrollerLinkHandler');

class 
comment_listing_v1_controller extends commentBaseController
{

   function 
comment_listing_v1_controller(){
   }

   function 
transformContent(){

      
// get category key
      
$this->__loadCategoryKey();

      
// get data layer component
      
$M = &$this->__getAndInitServiceObject('modules::comments::biz','commentManager',$this->__CategoryKey);

      
// load entries
      
$Entries $M->loadEntries();

      
$Buffer = (string)'';
      
$Template__ArticleComment = &$this->__getTemplate('ArticleComment');
      
$bbCP = &$this->__getServiceObject('tools::string','bbCodeParser');

      for(
$i 0$i count($Entries); $i++){

         
// fill template
         
$Template__ArticleComment->setPlaceHolder('Number',$i 1);
         
$Template__ArticleComment->setPlaceHolder('Name',$Entries[$i]->get('Name'));
         
$Template__ArticleComment->setPlaceHolder('Date',dateTimeManager::convertDate2Normal($Entries[$i]->get('Date')));
         
$Template__ArticleComment->setPlaceHolder('Time',$Entries[$i]->get('Time'));
         
$Template__ArticleComment->setPlaceHolder('Comment',$bbCP->parseText($Entries[$i]->get('Comment')));

         
// add template to list
         
$Buffer .= $Template__ArticleComment->transformTemplate();

       
// end for
      
}

      
// display hint in cas of no entries
      
if(count($Entries) < 1){
         
$Template__NoEntries = &$this->__getTemplate('NoEntries');
         
$Buffer $Template__NoEntries->transformTemplate();
       
// end if
      
}

      
// display list
      
$this->setPlaceHolder('Content',$Buffer);

      
// display pager
      
$this->setPlaceHolder('Pager',$M->getPager('comments'));

      
// get pager url params
      
$URLParameter $M->getURLParameter();

      
// generate create entry link
      
$this->setPlaceHolder(
                            
'Link',
                            
frontcontrollerLinkHandler::generateLink(
                                                                     
$_SERVER['REQUEST_URI'],
                                                                     array(
                                                                           
$URLParameter['StartName'] => '',
                                                                           
$URLParameter['CountName'] => '',
                                                                           
'coview' => 'form'
                                                                     
)
                            )
      );

    
// end function
   
}

 
// end function
Besonderheiten der Umsetzung sind das Holen des Kategorie-Schlüssels zu Beginn der Methode transformContent() und die dynamische Generierung des Eintragen-Links, da das Modul selbst nicht weiß, in welchem Bereich der Applikation es eingebunden ist. Für letztere Aufgabe wird die Business-Komponente um die Rückgabe der URL-Parameter gebeten, die in der Konfiguration unter 4.3. / Datei DEFAULT_pager.ini definiert worden sind. Die Parameter werden dann aus kosmetischen Gründen zurückgesetzt, damit der Pager beim Aufruf der Listing-Seite nach dem Eintragen immer auf die erste Seite springt. Dies könnte auch innerhalb des Managers abgebildet werden, wurde jedoch in den DocumentController verlagert, da hier ohnehin ein dynamischer Link zum Formular platziert werden muss. Im Fall der Kommentar-Funktion wird präventiv der frontcontrollerLinkHandler verwendet, obwohl die Funktion des linkHandler ausreichend wäre, da nicht mit Sicherheit davon ausgegangen werden kann, dass die aktuell generierte Seite nicht mit Hilfe von FrontController-Actions generiert wurde. Zur Formatierung der Ausgabe werden der bbCodeParser und der dateTimeManager eingesetzt.



5. Erweiterung der Software

Die Software, wie sie bis Ende des vierten Kapitels beschrieben wurde, könnte nun alle per PHPMyAdmin 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 Eintragen von Kommentaren schrittweise erweitert werden. Dazu gehen wir nun in der umgekehrten Reihenfolge vor und Beginnen mit der Präsentations-Schicht.


5.1. Präsentations-Schicht

Unter 4.4. 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. Das Formular gestaltet sich dann in XML-Tags ausgedrückt wie folgt:
  <html:form name="AddComment" method="post">
    <span style="margin-right: 10px;">Name:</span><form:text maxlength="100"
      name="Name" value="" class="eingabe_feld" style="width: 390px;" validate="true"
      button="Speichern" validator="Text" />
    <br />
    <span style="margin-right: 10px;">E-Mail:</span><form:text maxlength="100"
      name="EMail" value="" class="eingabe_feld" style="width: 390px;" validate="true"
      button="Speichern" validator="EMail" />
    <br />
    <br />
    Kommentar:
    <br />
    <form:area name="Comment" class="eingabe_feld" style="width: 438px; height: 120px; overflow: auto;"
      validate="true" button="Speichern" validator="Text" />
    <br />
    <br />
    <form:button name="Speichern" value="Speichern" class="eingabe_feld" />
  </html:form>
Mit den Attributen validate="true" und button="Speichern" wird die Formular-Validierung für dieses Feld aktiviert und mit validator="Text" bzw. validator="EMail" wird die Art und Weise festgelegt, mit der das Feld validiert werden soll. Mit ein wenig Text versehen hat das Template form damit folgenden Inhalt:
<@controller namespace="modules::comments::pres::documentcontroller" file="comment_form_v1_controller" class="comment_form_v1_controller" @>
<core:addtaglib namespace="tools::form::taglib" prefix="html" class="form" />
<core:addtaglib namespace="tools::html::taglib" prefix="html" class="getstring" />
<html:getstring namespace="modules::comments" config="language" entry="formhint.text.1" /> <a href="<html:placeholder name="Zurueck" />#comments" title="<html:getstring namespace="modules::comments" config="language" entry="formhint.text.2.title" />"><strong><html:getstring namespace="modules::comments" config="language" entry="formhint.text.2" /></strong></a><html:getstring namespace="modules::comments" config="language" entry="formhint.text.3" />
<br />
<br />
<html:placeholder name="Form"/>

<html:form name="AddComment" method="post">
  <span style="margin-right: 10px;"><form:getstring namespace="modules::comments" config="language" entry="form.name" />*</span><form:text maxlength="100" name="Name" value="" class="eingabe_feld" style="width: 390px;" validate="true" button="Save" validator="Text" />
  <br />
  <span style="margin-right: 10px;"><form:getstring namespace="modules::comments" config="language" entry="form.email" />*</span><form:text maxlength="100" name="EMail" value="" class="eingabe_feld" style="width: 390px;" validate="true" button="Save" validator="EMail" />
  <br />
  <br />
  <form:getstring namespace="modules::comments" config="language" entry="form.comment" />
  <br />
  <form:area name="Comment" class="eingabe_feld" style="width: 441px; height: 120px; overflow: auto;" validate="true" button="Save" validator="Text" />
  <br />
  <br />
  <span style="margin-right: 10px;"><form:getstring namespace="modules::comments" config="language" entry="form.confirm" />*</span>
  <br />
  <br />
  <img src="<form:placeholder name="CaptchaImage" />" border="0" align="absmiddle" />
  <form:text name="CaptchaString" class="eingabe_feld" style="margin-left: 10px; width: 60px;" validate="true" button="Save" validator="Text" maxlength="5" />
  <br />
  <br />
  <form:button name="Save" class="eingabe_feld" />
</html:form>
Der zugehörige DocumentController (comment_form_v1_controller) hat dabei die Aufgabe das Formular in den dafür vorgesehenen Platzhalter einzusetzen und im Fall eines abgeschickten und validen Formulars den Eintrag mit Hilfe der Business-Komponente zu speichern. Dazu implementiert dieser eine neue Methode saveEntry(), dem ein Domain-Objekt übergeben werden muss. Hier der Controller in der Übersicht:
import('modules::comments::pres::documentcontroller','commentBaseController');
import('tools::variablen','variablenHandler');
import('modules::comments::biz','commentManager');
import('tools::link','frontcontrollerLinkHandler');
import('tools::string','stringAssistant');

class 
comment_form_v1_controller extends commentBaseController
{

   var 
$_LOCALS = array();

   function 
comment_form_v1_controller(){
      
$this->_LOCALS variablenHandler::registerLocal(array('Name','EMail','Comment','CaptchaString'));
    
// end function
   
}

   function 
transformContent(){

      
// Referenz auf das Formular holen
      
$Form__AddComment = &$this->__getForm('AddComment');

      
// Prüfen, ob Formular abgesendet und erforlgreich validiert wurde
      
if($Form__AddComment->get('isSent') == true){

         
// Kategorie-Schlüssel laden
         
$this->__loadCategoryKey();

         
// Mapper holen
         
$M = &$this->__getAndInitServiceObject('modules::comments::biz','commentManager',$this->__CategoryKey);

         
// Validieren des Captchas
         
$CaptchaString $M->get('CaptchaString');

         if(
$CaptchaString != $this->_LOCALS['CaptchaString']){
            
$Captcha = &$Form__AddComment->getFormElementByName('CaptchaString');
            
$Captcha->set('isValid',false);
            
$Form__AddComment->set('isValid',false);
          
// end if
         
}

         
// Prüfen, ob Formular korrekt ausgefüllt wurde
         
if($Form__AddComment->get('isValid') == true){

            
// Eintrag erstellen
            
$ArticleComment = new ArticleComment();
            
$ArticleComment->set('Name',$this->_LOCALS['Name']);
            
$ArticleComment->set('EMail',$this->_LOCALS['EMail']);
            
$ArticleComment->set('Comment',$this->_LOCALS['Comment']);

            
// Eintrag speichern
            
$M->saveEntry($ArticleComment);

          
// end if
         
}
         else{
            
$this->__buildForm();
          
// end else
         
}

       
// end if
      
}
      else{
         
$this->__buildForm();
       
// end else
      
}

    
// end function
   
}

   function 
__buildForm(){

      
// Referenz auf das Formular holen
      
$Form__AddComment = &$this->__getForm('AddComment');

      
// action setzen
      
$Form__AddComment->setAttribute('action',$_SERVER['REQUEST_URI'].'#comments');

      
// Button beschriften
      
$Config = &$this->__getConfiguration('modules::comments','language');
      
$Button = &$Form__AddComment->getFormElementByName('Save');
      
$Button->setAttribute('value',$Config->getValue($this->__Language,'form.button'));

      
// CaptchaImage füllen
      
$Reg = &Singleton::getInstance('Registry');
      
$URLRewriting $Reg->retrieve('apf::core','URLRewriting');
      if(
$URLRewriting === true){
         
$Form__AddComment->setPlaceHolder('CaptchaImage','/~/modules_comments-action/showCaptcha');
       
// end if
      
}
      else{
         
$Form__AddComment->setPlaceHolder('CaptchaImage','./?modules_comments-action:showCaptcha');
       
// end else
      
}

      
// Formular darstellen
      
$this->setPlaceHolder('Form',$Form__AddComment->transformForm());

      
// Zurücklink darstellen
      
$Link frontcontrollerLinkHandler::generateLink($_SERVER['REQUEST_URI'],array('coview' => 'listing'));
      
$this->setPlaceHolder('Zurueck',$Link);

    
// end function
   
}

 
// end class

5.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 eine einfache Weiterleitung auf den Anzeigen-View. Hier muss natürlich darauf geachtet werden, dass die erzeugte Seite auch wieder korrekt angezeigt wird.
   function saveEntry($ArticleComment){

      
// Mapper holen
      
$M = &$this->__getServiceObject('modules::comments::data','commentMapper');

      
// Artikel speichern
      
$ArticleComment->set('CategoryKey',$this->__CategoryKey);
      
$M->saveArticleComment($ArticleComment);

      
// Auf die Ausgabe weiterleiten
      
$Link frontcontrollerLinkHandler::generateLink($_SERVER['REQUEST_URI'],array('coview' => 'listing'));
      
header('Location: '.$Link.'#comments');

    
// end function
   
Wie Zeile 6 ($ArticleComment->set('CategoryKey'..) zeigt manipuliert die Business-Schicht das Domain-Objekt, damit dieses mir der korrekten Kategorie gespeichert wird. Anschließend wird - wie bereits für die Generierung des Links für das Formular - der frontcontrollerLinkHandler für die Zusammensetzung der Weiterleitungs-URL verwendet.


5.3. Datenschicht

Die Datenschicht muss nun noch die Methode saveArticleComment() implementieren.
   function saveArticleComment($ArticleComment){

      
// SQL-Handler holen
      
$SQL = &$this->__getServiceObject('core::database','MySQLHandler');

      
// Prüfen, ob Artikel bereits existiert
      
if($ArticleComment->get('ID') == null){

         
$insert 'INSERT INTO article_comments
                    (Name, EMail, Comment, Date, Time, CategoryKey)
                    VALUES
                    (\''
.$ArticleComment->get('Name').'\',
                     \''
.$ArticleComment->get('EMail').'\',
                     \''
.$ArticleComment->get('Comment').'\',
                     CURDATE(),
                     CURTIME(),
                     \''
.$ArticleComment->get('CategoryKey').'\');';
         
$SQL->executeTextStatement($insert);

       
// end if
      
}

    
// end function
   
Die Bedeutung der Code-Zeilen lässt sich leicht erschließen. Zunächst wird eine Singleton-Instanz des MySQLHandler über die Methode __getServiceObject() bezogen und anschließend wird nach Prüfung ob es sich um einen neuen Kommentar handelt wieder in seine flache relationale Strukur zerlegt und per SQL-Statament gespeichert.



6. Ausblick / Ergänzung

An dieser Stelle möchte der Autor nochmals darauf hinweisen, dass die Methoden __getServiceObject() und __getAndInitServiceObject() immer dann Anwendung finden müssen, wenn das damit erzeugte Service-Layer Context-abhängige Konfigurationen läd oder ein Layer instanziiert, die weitere Context-abhängige Komponenten verwenden. Im Datenbank-gestützten Applikations-Design sollten deshalb grundsätzlich die beiden Methoden verwendet werden, da in letzter Konsequenz immer der MySQLHandler verwendet wird.


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.


Powered by WebRing.