Adressmanagement mit PEAR-Packages

Go Enterprise
Kommentare

Leider sind die Worte PHP und Enterprise bisher selten in einem Satz zu hören gewesen. Das mag vielleicht daran liegen, dass das richtige Vitamin für diese Verbindung bis dato noch nicht gefunden war. Es fehlte wohl an dem Framework, welches die Benutzung in großen Applikationen erleichtert. Eine moderne Birne, die das benötigte Vitamin enthält, ist jetzt endlich gefunden worden. Es ist natürlich eine englische Birne und heißt demzufolge PEAR. Mit dem PHP Extension and Application Repository (PEAR) existiert und entwickelt sich nun das Element, welches PHP dazu verhelfen wird, in den erlauchten Kreis der Enterprise-Sprachen vorzudringen.

Dieser Artikel wird PEAR und einige für diese Anwendung grundlegende PEAR-Packages vorstellen. Es wird darauf eingegangen, wie diese Packages eingesetzt werden, um die auf der beiliegenden CD zu findende Anwendung ContaX zu realisieren. ContaX [1], ein Open Source-Projekt, ist eine intelligente Kontaktdatenbank, die die Redundanz in der Verwaltung von Kontaktdaten minimiert und sich das Erstellen von Beziehungen zwischen verschiedenen Personengruppen zu Nutze macht (näheres zu diesem Konzept siehe Kasten Kontakte pflegen – nein danke). Zum Abschluss wird in einer kurzen Zusammenfassung verdeutlicht, wie durch den Einsatz von PEAR-Packages u.a. die Entwicklungszeit drastisch reduziert werden konnte.

PEAR

Seit PHP 4.3 ist PEAR nun endlich offiziell dem Betastadium entwachsen [2]. Dieser Artikel zeigt, wie mit Hilfe von PEAR eine Applikation in kürzester Zeit entsteht. PEAR ist in etwa vergleichbar mit den Java Foundation Classes, die eine strukturierte API mit umfangreicher Funktionalität bieten. Ein jedes PEAR-Package ist, anders als bei Java, ein unabhängiges Projekt, das von verschiedenen Personen betreut wird und seine eigenen Release-Zyklen hat. Für PEAR-Packages gibt es einige, immer wieder diskutierte, Regeln (z.B. Coding Standards), die eine hohe Qualität sichern sollen und der Vereinheitlichung der API und somit der Vereinfachung seines Einsatzes dienen. PEAR kann die Arbeit in allen Bereichen erleichtern: beim DB-Zugriff, bei der Authentifizierung, beim Formularhandling, bei der Internationalisierung oder auch beim Lösen spezieller Probleme wie z.B. bei der Bildmanipulation, im Mathematikbereich oder vielen anderen Bereichen.

Anwendung von PEAR-Packages und Alternativen

Zum Zugriff auf die DB wird das PEAR::DB-Package benutzt, das optional auch durch das neue Packet PEAR::MDB ersetzt werden können sollte, da die API fast gleich ist. Dies wäre aber zu testen. Um die SQL-Queries aufzubauen und an die DB zu senden, wird das Packet DB_QueryTool benutzt, das ein OO-Interface bietet, um Queries komfortabel zu erstellen. Die Klasse Sliding_Pager wird verwendet, um die seitenweise Anzeige der Daten zu verwalten. HTML_QuickForm wird benutzt, um die Formulare und deren Validierung zu behandeln. Des Weiteren kommen die Packages Tree, um Bäume zu verwalten, HTML_Template_Xipe als Template Lösung und HTML_TreeMenu, um die Navigation aufzubauen, zum Einsatz. Zur Internationalisierung wird das I18N-Package benutzt, dieses erleichtert u.a. die Formatierung des Datums, der Währung und auch die Übersetzung der Texte. Um zu demonstrieren, dass man auch Alternativen zu PEAR-Packages anbieten kann bzw. nicht unbedingt einen CVS-Zugriff bei PEAR haben muss, um ein PEAR-Package zu erstellen und genauso einfach benutzen zu können, kommt zur Authentifizierung ein alternatives Package, welches auf Sourceforge zur Verfügung steht, zum Einsatz [3]. Der Unterschied dieses Packages zu den Packages, die auf pear.php.net/ liegen, ist lediglich die Installation, die hier auf der Kommandozeile wie folgt aussieht:

pear install http://belnet.dl.sourceforge.net/sourceforge/auth/Auth-1.3-PEAR-package.tgz

Zur Abstraktion der benutzten Datenbank-API und für den flexiblen Einsatz auf verschiedenen Datenbanksystemen kommt hier das DB-Package zum Einsatz. Dieses Package ist schon (für ein PEAR-Package) seit sehr langer Zeit in einem stabilen Zustand. Es gibt viele verschiedene Lösungen einer DB-Abstraktion in PHP. PEAR::DB abstrahiert einerseits viele DB-Backends, schafft ein Interface zum Absenden der Queries und übernimmt einen Großteil der Aufbereitung der Ergebnisse. Dahingegen müssen wir hier die Auswahl der richtigen Typen der Tabellenspalten für jedes DB-Backend, das Handhaben spezieller Funktionalitäten verschiedener DBs (z.B. das Einfügen eines BLOBs in eine Oracle-DB) und die Abstraktion der SQL-Dialekte immer noch selbst in die Hand nehmen. Einen Schritt weiter geht da das Package MDB, das z.B. die Datentypen vereinheitlicht. Ausführungen, inwieweit es alle oben erwähnten Aspekte berücksichtigt, können unter anderem in [4] nachgelesen werden. Da unsere Anwendung mit SQL89 (SQL1) auskommt und wir keine BLOBs benötigen, können wir in Ruhe auf das DB-Package aufbauen. Der Aufbau der Queries wird in unserer Applikation von dem DB_QueryTool-Package übernommen. Da z.B. eine DB wie Oracle im Gegensatz zu MySQL kein auto-increment kennt, benutzen wir die Sequenzen, die uns das DB-Package mit Hilfe der Methode nextId zur Verfügung stellt. Damit werden wir kompatibel zu Oracle, PostgreSQL, etc. und für MySQL wird eine Sequenz emuliert, indem eine zusätzliche Tabelle für jede Tabelle angelegt wird, die nur den Wert der Sequenz enthält. Als positiven Nebeneffekt haben wir gleichzeitig ein Problem umgangen, das bei MySQL so manches Doku-lesen mit sich brachte, nämlich das Indizes nie doppelt und nie wieder benutzt werden.

1 $DB_DSN = 'mysql://root:passwd@localhost/dbName';
2 $db = DB::connect($DB_DSN);
3 if (DB::isError($db))
4 die( $db->getMessage() );
5 // db-connect succeeded
6 // get the first 10 users
7 $db->setFetchMode(DB_FETCHMODE_ASSOC);
8 $query = 'SELECT * FROM user';
9 $query = $db->modifyLimitQuery($query,0,10);
10 $users = $db->getAll($query);

Durch den Einsatz des Packages DB_QueryTool wird das Auslesen von Daten aus der DB um ein Vielfaches einfacher, außerdem bleibt der Code auch beim Zusammenbauen komplizierter Queries noch übersichtlich und wartbar. Da das Modifizieren und Abfragen von Daten aus der DB den Großteil einer DB-basierten Applikation ausmacht, haben wir uns bei vision:produktion dazu entschlossen, diese Klassen zu erstellen. Nachdem sie über fast ein Jahr hinweg stetig verbessert wurden und bei uns auch im produktiven Einsatz überzeugen, haben wir uns entschlossen, diese Klassen über PEAR zur Verfügung zu stellen. DB_QueryTool ist ein intelligenter SQL-Builder mit zusätzlicher Funktionalität wie z.B. dem Validieren der Queries bevor Sie abgeschickt werden, damit SQL-Fehler vermieden werden und die Faulheit der Programmierer endlich auch einmal berücksichtigt wird. In den folgenden Beispielen gehen wir davon aus, dass das benutzte Objekt von der Klasse DB_QueryTool abgeleitet ist und für den gleichnamigen Tabellennamen initialisiert wurde sowie dass der Primärschlüssel in der Spalte mit dem Namen id abgelegt ist. $contact also bezieht sich auf die Tabelle contact. Um die Zeile mit der id=47 auszulesen, ist lediglich der Aufruf

$contact >setWhere("name='Bob'");
$contact >setOrder('surname');
$bobs = $contact >getAll(0,10);

Wir bauen einen Query, der für MySQL wie folgt aussehen würde, zusammen: select * from contact where name=’Bob‘ order by surname limit 0,10. In der Variable $bobs erhalten wir maximal die ersten zehn Datensätze aus der DB, die diesem Query entsprechen. Ein weiteres etwas komplexeres Beispiel wurde derApplikation entnommen und etwas vereinfacht (siehe classes/modules/contact.php). Obwohl die Logik in diesem Query etwas komplexer ist, bleibt der Aufbau einfach und übersichtlich:

1 $contact->reset();
2 $contact->autoJoin(array('contact2tree','contactTree'));
3 $contact->setLeftJoin('email','email.contact_id=id');
4 $contact->setWhere('contactTree.user_id=1');
5 $contact->setOrder('surname,name');
6 $contacts = $contact->getAll(0,10);

Hier wird ein Query zusammengebaut und abgeschickt, drei Tabellen werden zusätzlich gejoint, eine Selektion der Daten via setWhere wird getroffen und eine Sortierung vorgenommen. In Zeile 1 wird der Ausgangszustand des Objektes $contact hergestellt. Dieses Objekt wurde so initialisiert, dass es primär auf der Tabelle contact arbeitet, d.h. würden wir in Zeile 2 $contact >getAll() aufrufen, wäre dies gleichbedeutend mit dem Absenden des folgenden Queries: select * from contact. Wir wollen aber einen etwas umfangreicheren Datensatz bekommen. Dazu joinen wir in Zeile 2 zwei weitere Tabellen. Die Funktion autoJoin baut den Join selber intelligent zusammen, in dem sie in jeder der Tabellen nach Referenzen zueinander sucht. Dies wird über die Spaltennamen nach dem Schema table_id gemacht, um diese entsprechend zu joinen (das Schema ist natürlich modifizierbar). In Zeile 3 fügen wir einen Left-Join der Tabelle email hinzu. Hier wird ein Left-Join benutzt, da wir nicht sicher sein können, dass der Eintrag in der Tabelle contact auch eine Referenz in der Tabelle email besitzt. Berechtigt fragt jetzt jeder, warum wir nicht einfach die Methode autoLeftJoin dafür verwendet haben. Aus dem einfachen Grund, da diese noch nicht implementiert ist und somit noch ein To-Do darstellt. Nachdem wir einen where– und einen order-Teil hinzugefügt haben, schicken wir den Query an die Datenbank und erwarten das Ergebnis in der Variable $contacts. Hierbei haben wir durch die Parameter 0,10 angegeben, dass wir nur an den ersten zehn Datensätzen interessiert sind. Wir erhalten somit ein zweidimensionales Array, wodurch über $contacts[0] der erste Datensatz des Ergebnisses erreichbar ist. Dieser ist wiederum ein Array, das die Daten aller angeforderten Tabellen in einem assoziativen Array beinhaltet. Damit doppelte Indizes in dem Array verhindert werden, haben die Spalten der gejointen Tabellen den Index nach dem Schema _table_spalte bekommen. Somit kann z.B. auf die Spalte name aus der Tabelle contactTree im dritten Datensatz wie folgt zugegriffen werden: $contacts[2][‚_contactTree_name‘]. Das Hinzufügen (insert), Löschen (delete) sowie Ändern (update) von Zeilen in der DB erfolgt genau so einfach. Dafür stehen u.a. die Methoden save, add, update und remove zur Verfügung. Die Methode save ist hierbei ein Sonderfall. Diese Methode ruft entweder update oder add auf, je nachdem, ob ein Wert für den Primärschlüssel übergeben wurde. D.h. in unserem Fall: übergeben wir an save das Array array(‚id’=>1,’name’=>’Bob‘), so wird in die Spalte name in der Zeile mit der id=1 der Wert ‚Bob‘ geschrieben, dazu ruft die save-Methode update auf. Würde keine id übergeben werden, so wird von der save-Methode add aufgerufen, die eine neue Zeile in die Tabelle einfügt und als positives Resultat die id der neuen Spalte zurückgibt. Die Methoden add und update können natürlich auch explizit aufgerufen werden. Aus den zahlreichen weiteren Methoden möchte ich nur noch setDontSelect und setIndex kurz vorstellen. Beim Auslesen von Tabellen, die ein BLOB enthalten, kann es vorkommen, dass man nicht in jedem Fall an den BLOB-Daten interessiert ist und auch das Transfervolumen nicht unbedingt strapazieren möchte. Um nur bestimmte Spalten aus einer Tabelle zu erhalten, kann man die Methode setSelect benutzen und ihr die Spalten übergeben, die man benötigt, was bei einer sich ändernden Tabellenstruktur auch ein Ändern des Parameters der setSelect-Methode mit sich bringen würde. Um aber z.B. in der Entwicklungsphase sicherzustellen, dass immer alle Spalten außer der BLOB-Spalte ausgelesen werden, wäre es einfacher, mit setDontSelect die BLOB-Spalte nicht in das Resultat mit einzubeziehen. Die Methode setIndex ermöglicht es, das Array, welches z.B. von getAll zurückgegeben wird, so aufzubereiten, dass es als Index für einen Datensatz den Wert einer bestimmten Spalte hat. Damit ist die direkte Referenzierung eines Datensatzes viel einfacher. Ein Aufruf $contact >setIndex(’name‘) hat zur Folge, dass z.B. ein Zugriff auf den Datensatz von Bob über $contacts[‚Bob‘] möglich wird. Es können auch mehrere Spalten als Index benutzt werden.

1 $options = array(
2 'expire' =>1*60*60, // 1 hour
3 'protectRoot'=>'/modules',
4 'dontProtect'=>array('/user/register','*.css.*'),
5 'loginPage' =>'user/login.php',
6 'errorPage' =>'/user/login.php?loginFailed=true',
7 'defaultPage'=>'/user/login.php',
8 'table' =>'user',
9 'digest' =>'md5');
10 $userAuth = Auth::setup('DB',$db,$options);
11 if (Auth::isError($userAuth))
12 die( $userAuth );
13 $userId = $userAuth->getData('id');

Die Authentifizierung überprüft an Hand der aktuellen URL, ob der Zugriff eine Anmeldung erfordert. Ist dies der Fall, leitet sie automatisch auf die loginPage (Zeile 5) um. Nach einer erfolgreichen Anmeldung kommt der Benutzer auf die von ihm eigentlich angeforderte Seite. In der Option dontProtect haben wir angegeben, dass alle Stylesheets sowie die Seite, auf der sich Benutzer registrieren können, nicht geschützt sein sollen. Alle anderen Seiten, die sich in dem Verzeichnis modules befinden, benötigen eine erfolgreiche Anmeldung. In Zeile 10 wird die Authentifizierung initialisiert. Die Variable $db ist hier der Einfachheit halber ein Objekt der PEAR::DB-Klasse. Sie könnte genauso gut auch eine gültige DSN sein. Nach dem erfolgreichen Authentifizieren holt die Auth-Klasse automatisch den kompletten Datensatz aus der Tabelle user, die wir für die Authentifizierung angegeben haben. Damit ist ein Zugriff auf die User-relevanten Daten jederzeit möglich, wie in Zeile 13 zu sehen. Als Parameter der Methode getData braucht nur der Spaltenname übergeben zu werden. Wurde die Authentifizierung nicht gegen eine DB gemacht und die Daten aus der DB sollen trotzdem in der Auth-Instanz vorhanden sein, so kann man einfach mit setData beliebige Daten an die Auth-Instanz übergeben, die dafür Sorge trägt, dass diese persistent gespeichert werden und via getData wieder zur Verfügung stehen. Dabei ist einzig zu beachten, dass Sie nicht schon darin gespeicherte Daten ungewollt überschreiben. Ein kleiner Tipp: durch den Aufruf der Methode getData ohne Parameter bekommen Sie sämtliche persistente Daten in einem Array zurück.

Bäume verwalten

Mit Hilfe des Tree-Packages können Bäume verwaltet werden. Dabei werden zwei grundlegende Vorgehensweisen unterschieden. Die eine ist für relativ kleine Bäume gedacht, um z.B. eine Navigation aufzubauen oder kleinste Ordnerstrukturen darzustellen. Diese so genannte Memory-Version liest den gesamten Baum in ein Array ein, um diesen anzuzeigen bzw. verwalten zu können. Da dabei der gesamte Speicher, der für den Baum benötigt wird, allokiert werden muss, wird diese Variante Memory-Variante benannt. Die andere Vorgehensweise (Dynamic) ist für beliebig große Bäume sinnvoll einsetzbar. Dabei wird jeweils nur ein Teil des Baumes ausgelesen, wie von verschiedenen Onlinearchiven bekannt (z.B. dmoz.org/). Um diesen Baum in einer DB abzulegen, ist das Nested-Set Modell [5] bestens geeignet, dieses ist u.a. in dem Tree-Package implementiert und kommt in der hier beschriebenen Anwendung zum Einsatz. Das Tree-Package kann auch Bäume in Arrays und XML-Dat(ei)en bearbeiten. Die bisherige Implementierung erlaubt aber nur einen lesenden Zugriff auf letztere. Zur Erstellung der Navigation wird ein Baum aus einem Array ausgelesen. Hierbei wäre es, durch die einheitliche API, genauso gut möglich, diesen Baum in einer XML-Datei abzulegen oder aus der DB auszulesen. Die Entscheidung für das Array wurde aus Gründen der Einfachheit getroffen. Soll die Navigation einmal über ein Frontend verwaltet werden, so kann man die Daten auch in eine DB schreiben, um diese in einem persistenten Speicher vorzuhalten. In dem folgenden Beispiel wird kurz demonstriert, wie eine Tree-Instanz erzeugt wird, und ein Baum verändert werden kann.

1 require_once 'Tree/Tree.php';
2 $DB_DSN = 'mysql://root@localhost/ContaX';
3 $options = array('table'=>'contactTree');
4 $tree = Tree::setup('Dynamic_DBnested',$DB_DSN,$options);
5 $rootId = $tree->getRootId();
6 $newData = array('name'=>'my Friends');
7 $friendsId = $tree->add($newData,$rootId);
8 $newData = array('name'=>'Friends');
9 $tree->update($friendsId,$newData);
10 $newData = array('name'=>'England');
11 $englandId = $tree->add($newData,$rootId);
12 $tree->move($friendsId,$englandId);
13 echo $tree->getPathAsString($englandId);
14 $tree->remove($englandId);

Das Beispiel erstellt den im Kasten Kontakte pflegen – nein danke erwähnten Zweig eines Baumes. In Zeile 4 ist zu sehen, dass eine Instanz der Klasse Tree, zur dynamischen Verwaltung eines Baumes, der als Nested-Set in der DB-Tabelle contactTree abgelegt ist, erzeugt wird. Es wird vorausgesetzt, dass das Root-Element bereits in der DB angelegt wurde. In Zeile 7 wird ein neuer Zweig in den Baum eingefügt. Dieser Zweig wird mit den Daten aus dem in Zeile 6 gefüllten Array unter dem Root-Element mit der ID $rootId angelegt. Das assoziative Array enthält in dem Index name den Wert für die gleichbenannte Tabellenspalte. Die add-Methode gibt bei einer erfolgreichen Ausführung (die hier der Übersichtlichkeit wegen, ohne eine Fehlerbehandlung, vorausgesetzt wird) die ID des eingefügten Elements zurück. Nach dem selben Schema wird in Zeile 9 das Feld dieses Eintrages noch einmal modifiziert, wobei als erster Parameter die ID des Zweiges übergeben wird. In Zeile 11 wird ein neuer Zweig mit dem Namen ‚England‘ angelegt. Dieser wird auf der selben Ebene erstellt wie der vorherige Zweig, direkt unter dem Root. Danach wird in Zeile 12 der Zweig mit der ID $friendsId unter den mit der ID $englandId verschoben. Dadurch entsteht ein zwei Ebenen tiefer Baum, der als Pfad wie folgt aussehen würde: /Root/England/Friends. Diesen Pfad kann man sich, wie in Zeile 13 dargestellt, ebenfalls als String ausgeben lassen. Schließlich vernichten wir noch die gesamte getane Arbeit, indem wir alle Elemente einschließlich ‚England‘ löschen (Zeile 14). Abschließend sollte noch erwähnt werden, dass das Tree-Package aktuell in der Version 0.2.1 verfügbar ist. Diese niedrige Versionsnummer rührt daher, dass noch nicht alle Datenquellen komplett implementiert sind. Es können zumindest Memory_DBnested sowie Dynamic_DBnested als stabil angesehen werden und sind auch schon im produktiven Einsatz zu finden. Weitere Datenquellen zu implementieren sowie das Fertigstellen der Verwaltung von XML-Dateien sind u.a. die nächsten Ziele.

Kontakte pflegen – nein danke

Die eigentliche Idee zu dieser Applikation entsprang natürlich dem wirklichen Leben. Ich wollte einen Freund anrufen, dessen Telefonnummer ich nicht hatte. Ich wusste aber, das ein anderer Freund mir die Nummer geben könnte. Also rief ich ihn an und lies mir die Nummer geben, nur um Ersteren anzurufen. Danach dachte ich mir, dass dies nur der Telekom genutzt hatte und doch eigentlich viel einfacher ginge. Was ich im Grunde genommen gemacht habe, war indirekt in das Adressbuch eines Freundes zu gucken, nur dass ich ihn dazu anrief. Würde er sein Adressbuch online pflegen und meine Daten sowie die Daten des Freundes, den ich eigentlich anrufen wollte, darin enthalten sein, so hätte er mir einfach nur ein Leserecht auf die Daten zu geben brauchen. Damit hätte ich Zugriff auf Daten, die er für mich freigibt. Nun wird niemand sein komplettes Adressbuch offen legen und allen Freunden beliebigen Zugriff darauf gewähren. Also sortiert jeder Nutzer die Kontakte in Gruppen wie z.B. Freunde, Familie, etc. Diese werden so gewählt, dass in einer Gruppe immer die Leute sind, denen man gegenseitig vertraut und damit wird diesen Personen erlaubt, ihre Kontaktdaten gegenseitig einzusehen. Werden diese Gruppen in einem Baum (wie Ordner auf einem Dateisystem) organisiert, erhält man eine sehr flexible Struktur und kann die Rechte viel granulierter über mehrere Ebenen verteilen. Das Recht, Kontakt einzusehen, vererbt sich implizit auf den User, dessen Kontakt in einem Ordner abgelegt ist. D.h. wenn ich die Daten von Bob und Alice in dem Ordner privat/Freunde/England ablege, dann erlaube ich damit Alice und Bob meine sowie jeweils auch die Daten des anderen zu sehen. Hat Alice wiederum die Daten von John im selben Ordner wie meine Daten, so wäre ich auch in der Lage, Johns Daten zu sehen. Bob wäre dazu aber nicht in der Lage, da die implizite Rechtevergabe nur über eine Ebene sinnvoll ist. Würde ich jetzt John ebenfalls in den Ordner, in dem ich Alice und Bob habe, aufnehmen, so würden diese drei automatisch gegenseitig ihre Daten einsehen können. Jetzt wird deutlich, dass durch das Aufnehmen von Kontaktdaten in verschiedene Ordner redundante Datensätze entstehen. Dies kann natürlich durch die Referenzierung der Originaldatensätze verhindert werden. Bloß wer verwaltet dann die Daten? Im Idealfall jede Person selber und zwar nur ihre eigenen Kontaktdaten. Damit müsste sich niemand, der diese Daten referenziert, um die Änderung einer Telefonnummer oder ähnliche Belange kümmern, da dies die referenzierte Person selber tut. Das Aufnehmen einer Person in einen bestimmten Ordner würde durch ein einfaches Referenzieren des Datensatzes geschehen, soweit diese Person dies erlaubt. Damit ergibt sich in letzter Konsequenz auch die Schlussfolgerung, dass diese Anwendung im Idealfall nur einmal auf der Welt installiert zu sein bräuchte. Durch diesen Umstand muss die Anwendung als verteilte Anwendung realisiert werden. Ein weiterer Grund wäre die Lastverteilung und auch die Datensicherheit, um jedem User die Möglichkeit zu geben, seine eigenen Daten ausschließlich auf seinem eigenen Server abzulegen und höchstmögliche Sicherheit zu gewährleisten. Eine weitere Schlussfolgerung ist, dass dieses Projekt nur als freie Anwendung eine Verbreitung finden kann. Eine Anbindung an mobile Endgeräte, Benutzung der Datensätze zur Konfiguration von Spamfiltern und weitere Anwendungsfälle sind denkbar.

Umsetzung

Nachdem wir uns jetzt einige der wichtigsten Teile der Anwendung etwas näher angeschaut haben, werden wir im Folgenden einen genaueren Blick auf die Applikation selbst werfen. Die Verzeichnisstruktur gliedert sich in einen im Web sichtbaren Teil im Verzeichnis htdocs und alle unsichtbaren Dateien und Verzeichnisse. Die wichtigsten sind wohl das classes-Verzeichnis, das die Klassen mit der Applikationslogik enthält, sowie das includes-Verzeichnis, welches alle installierten (PEAR-) Packages, die per ini_set(‚include_path‘,…) in die Applikation eingebunden werden, beinhaltet. Die Klassen mit der Applikationslogik sind zumeist auf eine DB-Tabelle gemappt. Diese Klassen sind von der Klasse DB_QueryTool abgeleitet, um wie weiter oben besprochen den Zugriff auf die DB zu erleichtern. Dabei ist die Klasse modules_contactTree eine Besonderheit. Sie ist direkt von der Tree-Klasse Tree_Dynamic_DBnested abgeleitet, um zusätzlich benötigte Funktionalität in dieser Klasse zu implementieren. In diesem konkreten Fall ist dies bisher die Funktion get2Levels, die zum Auslesen und Aufbereiten der Daten, welche zur Darstellung der Ordner benötigt werden, benutzt wird. In der Tabelle contactTree legen wir die Ordner der einzelnen User ab. Jeder Benutzer hat sein eigenes Root und erstellt seine individuelle Baumstruktur. Über diese Ordner und die darin referenzierten Kontakte werden die erwähnten Referenzen zwischen den einzelnen Usern hergestellt und es werden alle für einen User sichtbaren Kontakte ausgelesen.

Fazit

Durch die Wahl der PEAR-Packages und das Verbinden dieser mittels der entsprechenden Logik ist der Prototyp der Applikation ContaX in einem kurzen Zeitraum entstanden. Der Zugriff auf die DB, das Erstellen der Queries, das Verwalten von Bäumen, das Erstellen von Formularen und viele der kleinen Stücke, aus denen eine Webanwendung besteht, mussten nicht wieder von neuem implementiert werden. Die auf der CD beiliegende Anwendung ist nur ein erster Snapshot des Standes, der zu diesem Zeitpunkt existiert. Bis zum Erscheinen der Zeitschrift wird ContaX auf jeden Fall weiterentwickelt worden sein, daher lohnt es sich immer, einen Blick auf contax.visionp.de/ zu werfen und den neusten Stand runterzuladen bzw. auch mit eigenen Ideen und Verbesserungen die Anwendung zu beeinflussen. Dieser Artikel sollte einen kleinen Einblick in ein paar der verwendeten Packages geben und zeigen, dass deren Einsatz einfach ist und den großen Vorteil mit sich bringt, auf Komponenten zurückgreifen zu können, die schon von vielen anderen Programmierern eingesetzt und stetig verbessert werden. Es lohnt sich, einen Blick in die existierenden Packages auf [2] zu werfen und wenn es auch nur eine Hand voll sind, die man benötigen würde: eine deutliche Reduzierung des Programmieraufwandes und das schnellere Erreichen des Zieles sind garantiert. Und Vitamine sind ja bekanntlich auch wichtig im Leben. Dipl.-Ing. Wolfram Kriesing ist Leiter der Programmierung bei der vision:produktion GmbH [6] in München und für die Entwicklung von Webapplikationen zuständig. Als aktiver Entwickler im Open Source-Umfeld ist er maßgeblich an einigen PEAR-Packages beteiligt.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -