15.4 DB_Table 

Besprochene Version: 1.0.0 | Lizenz: LGPL |
Klassendatei(en): DB/Table.php |
DB_Table ist ein interessantes Paket. Ähnlich wie DB_Querytool stellt es einen Abstraktionslayer für PEAR::DB dar, so dass Sie sich selbst nicht mit SQL-Befehlen beschäftigen müssen. Allerdings ist DB_Table leistungsfähiger und bietet als besonderes Schmankerl eine direkte Integration von PEAR::HTML_QuickForm an.
Um mit dem Paket arbeiten zu können, gilt auch hier wieder, dass Sie natürlich schon eine gute Vorstellung von SQL und seinen Möglichkeiten haben sollten, um das Paket sinnvoll zu nutzen.
Die Methoden der Klasse werden üblicherweise nicht direkt aufgerufen. Das Paket sieht vor, dass eine neue Klasse von der Klasse DB_Table abgeleitet wird. In dieser Subklasse, die alle Methoden und Eigenschaften der Elternklasse erbt, definieren Sie den Aufbau der Tabelle, die Abfragen und Ähnliches.
Leiten Sie ein Objekt der Klasse ab, ist diesem eine bestehende Datenbankverbindung in Form eines DB-Objekts sowie der Name der Tabelle zu übergeben, mit der gearbeitet werden soll. Optional kann mit einem dritten Parameter definiert werden, ob die Tabelle neu angelegt werden soll.
Die folgenden Beispiele basieren auf einer »Telefonliste«. Diese soll den Namen, die Telefonnummer und ein Feld für Kommentare enthalten.
require_once('DB/Table.php'); require_once('DB.php'); class class_liste extends DB_Table { // Aufbau der Tabelle public $col = array ( 'id' => array ( 'type' => 'integer', 'require' => true ), 'vname'=>array ( 'type' => 'varchar', 'size' => 20 ), 'nname'=>array ( 'type' => 'varchar', 'size' => 20, 'require'=>true ), 'fon'=> array ( 'type' => 'varchar', 'size' => 20 ), 'kommentar'=>array ( 'type' => 'clob') ); // Definition des Schluessels public $idx = array ( 'id' => array ( 'type'=>'unique', 'cols'=>'id') ); //Der Konstruktor ruft den Konstruktor der Elternklasse auf function __construct($con, $tabelle, $create = false) { parent::DB_Table($con, $tabelle, $create); if ('drop'===$create) { $con->dropSequence($tabelle); } if (false != $create) { $con->createSequence($tabelle); } } } // Verbindung zur Datenbank aufbauen $dsn = 'mysql://user:geheim@localhost/datenbank'; $tabelle = 'telefonliste'; $con = DB::connect ($dsn); // Neues Objekt ableiten, das automatisch die Tab erstellt $tab = new class_liste($con,$tabelle,'safe'); if (true == PEAR::isError($tab->error)) { die ("Fehler: ".$tab->error->getMessage()); }
Listing 15.9 Ableiten einer Klasse für DB_Table
Jede Klasse, die Sie aus DB_Table ableiten, ist für eine Tabelle zuständig. In der Klasse ist eine Eigenschaft namens $col zu definieren, die den Aufbau der Tabelle festlegt. Für jede Tabellenspalte ist ein eigenes Array anzulegen, das unter dem Spaltennamen abgelegt wird.
In diesem Array wird spezifiziert, wie die Spalte aufzubauen ist. Der Schlüssel 'type' muss vorhanden sein und legt fest, welchen Datentyp die Spalte haben soll. Allerdings können Sie nicht alle Datentypen nutzen, die Ihnen vielleicht spontan einfallen. Da das Paket die Kommunikation mit der Datenbank für Sie übernehmen soll, müssen die Felder natürlich vereinheitlicht werden. Hierzu sind abstrakte Datentypen definiert, die für die Darstellung in den Datenbanken entsprechend konvertiert werden. Zulässig sind die Datentypen aus Tabelle 15.6.
Bei den Datentypen, bei denen das erforderlich ist, können Sie die Feldlänge mit dem Schlüssel 'size' festlegen. Der Datentyp 'decimal' erfordert darüber hinaus noch eine Angabe der Nachkommastellen mit 'scope'. Geben Sie in diesem Fall also beispielsweise eine Länge von 5 und einen Nachkomma-Anteil von 2 an, so können Sie alle Zahlen von -999.99 bis 999.99 damit darstellen.
Für die Definition der Spalten können Sie zusätzlich noch die Schlüssel 'required' und 'default' nutzen. Dem ersten können Sie einen booleschen Wert übergeben, um festzulegen, ob das Feld erforderlich ist, und mit dem zweiten können Sie einen Wert festlegen, der standardmäßig in das Feld eingefügt wird, wenn kein anderer Wert angegeben wird.
Welche Spalten indiziert werden sollen, können Sie über die Eigenschaft $idx steuern. Jeder Index bekommt einen eigenen Namen, der als Array-Schlüssel fungiert. Der Schlüssel verweist jeweils auf ein anderes Array, das die Schlüssel 'type' und 'cols' beinhaltet. Der erste Schlüssel kann entweder 'unique' oder 'normal' als Wert haben. Mit 'unique' stellen Sie sicher, dass jeder Wert bzw. jede Wertkombination innerhalb des Index nur einmal vorkommen darf. Mit dem anderen Wert sehen Sie einen »normalen« Index vor.
Über welche Spalten der Index sich erstreckt, wird mit dem Schlüssel 'cols' definiert. Hier können Sie entweder einen einzelnen Spaltennamen direkt angeben, oder Sie nutzen ein Array mit Spaltennamen, wenn der Index mehrere Spalten umfassen soll.
Der Konstruktor, der definiert wurde, müsste nichts anderes machen, als den Konstruktor der Elternklasse aufzurufen. Er muss drei Werte akzeptieren. Neben dem DB-Objekt für die Verbindung und dem Namen der Tabelle wird noch ein dritter Parameter unterstützt. An dieser Stelle können Sie entweder 'safe', 'drop' oder false übergeben und somit steuern, ob die Tabelle neu angelegt wird. 'safe' legt die Tabelle nur dann neu an, wenn sie noch nicht existiert. Mit 'drop' wird die Tabelle auf jeden Fall gelöscht. Das geschieht auch dann, wenn schon Daten enthalten sind. Übergeben Sie keinen Wert oder false, so wird nicht versucht, die Tabelle neu anzulegen.
Wie Sie sehen, ruft der Konstruktor aber nicht nur den Konstruktor der Elternklasse auf, sondern legt auch noch eine Sequenz mithilfe des Pakets DB an. Diese kleine Erweiterung platziere ich gern im Konstruktor, um die Verwaltung der Sequenz, die die Werte für die ID liefert, zu automatisieren. DB_Table selbst bietet keine automatische Verwaltung für Sequenzen.
15.4.1 Einfügen von Werten 

Um Werte in die Tabelle einzufügen, ist die Methode insert() vorgesehen. Diese bekommt ein Array übergeben, bei dem die Namen der Spalten als Schlüssel genutzt werden und auf die entsprechenden Werte verweisen. Im einfachsten Fall könnte der Aufruf beispielsweise so erfolgen:
$tab->insert(array( 'id' => '1', 'vname' => 'Moghi', 'nname' => 'Maus', 'fon' => '104362', 'kommentar' => '' ) );
Allerdings würde ich Ihnen empfehlen, diese Methode in Ihrer Klasse zu überschreiben. Das bietet den Vorteil, dass Sie dann auch gleich den Zugriff auf die Sequenz in die Methode einbinden können. Da der Name der Sequenz sich aus dem Namen der Tabelle ableitet, ist der Zugriff recht einfach. Der Tabellenname ist in der Eigenschaft table abgelegt, so dass er direkt genutzt werden kann. Eine solche Methode könnte so aussehen:
public function insert ($data) { // Wert aus der Sequenz holen $id_arr= array( 'id' => $this->db->nextId($this->table) ); // Arrays zusammenfuehren $complete =array_merge($id_arr,$data); // Methode der Superklasse aufrufen return parent::insert($complete); }
Diese Methode ermittelt selbstständig den nächsten Wert aus der Sequenz, so dass das Array, das übergeben wird, nur die eigentlichen Werte enthalten muss.
Die Methode insert() prüft die Werte auf Basis der Spaltendefinitionen auch sofort auf Validität. Sollte ein Fehler auftreten, wird die Eigenschaft error mit einem entsprechenden PEAR_Error-Objekt belegt.
15.4.2 Nutzung von Formularen 

Da Sie die Daten, die in die Tabelle eingefügt werden sollen, aber wahrscheinlich eher selten direkt im Quelltext angeben, sieht das Paket vor, auf Basis von HTML_QuickForm automatisch ein Formular generieren zu lassen. Da DB_Table den Aufbau der Datenbank-Tabelle kennt, kann das Paket die HTML-Felder, die typischerweise für einen bestimmten Datentyp genutzt werden, automatisch generieren.
Die schnellste Möglichkeit, um ein Formular auszugeben, wären diese beiden Zeilen:
$form = $tab->getForm(); $form->display();
Listing 15.10 Automatisches Generieren eines Formulars
In diesem Fall würde das Formular direkt auf Basis der Tabelle generiert. Die Beschriftung der Felder entspricht den Spaltennamen, und es würde keinen Button zum Abschicken geben. In Tabelle 15.7 finden Sie eine Aufstellung, welcher Datentyp in welche Darstellung konvertiert wird. Weitere Informationen zu den QuickForm-Datentypen finden Sie in Abschnitt 17.9.
Wie erwähnt, ist allerdings noch kein Button zum Absenden enthalten. Da die Methode getForm() aber ein HTML_QuickForm-Objekt zurückgibt, stellt das kein Problem dar. Sie können also einfach mit der QuickForm-Methode addElement() weitere Elemente ergänzen.
Allerdings werden die Felder nach wie vor mit dem Spaltennamen beschriftet. Das können Sie bei der Deklaration der Klasse beeinflussen. In dem Array $col, in dem Sie die Spalten definiert haben, können Sie für jede Spalte noch zusätzliche »QuickForm-Schlüssel« nutzen.
Mit dem Schlüssel 'qf_label' können Sie eine Beschriftung für das entsprechende Formularelement vorgeben, so dass nicht der Spaltenname genutzt wird. Möchten Sie einen anderen Element-Typ nutzen, können Sie diesen mit dem Schlüssel 'qf_type' festlegen. Als Wert können Sie dabei alle Element-Typen nutzen, die Quickform kennt.
Welche Werte eine Radio-Gruppe, eine Optionsliste oder eine Checkbox zurückgeben soll, können Sie mit 'qf_vals' definieren. Diesem Schlüssel müssen Sie ein Array übergeben, in dem die Werte spezifiziert sind. Bei einer Checkbox übergeben Sie nur die beiden möglichen Werte.
'werbung'=>array ( 'type' => 'char', 'size' => 1, 'qf_type' => 'advcheckbox', 'qf_label' => 'Ja, ich möchte Werbung', 'qf_vals'=>array ('N', 'J') )
Der erste Wert, den Sie dabei übergeben, ist der Wert, den die Checkbox liefert, wenn sie nicht angeklickt wurde. Bei Nutzung einer normalen Checkbox wird der erste Wert ignoriert.
Für eine Gruppe von Radiobuttons oder eine Optionsliste nutzen Sie ein Array, bei dem die Schlüssel die eigentlichen Werte darstellen. Der Wert des Elements wird dann als Beschriftung genutzt.
'werbung'=>array ( 'type' => 'char', 'size' => 1, 'qf_type' => 'select', 'qf_label' => 'Dürfen wir Ihnen Werbung schicken?', 'qf_vals'=>array ( 'N' => 'Nein, ich möchte keine Werbung', 'J' => 'Ja, bitte informieren Sie mich' ) )
Die letzte Möglichkeit, das Erscheinungsbild zu beeinflussen, ist der Schlüssel 'qf_attrs'. Er ermöglicht es Ihnen, frei definierte Attribute an das Formular-Element zu übergeben. Der Name des Attributs wird dabei als Schlüssel genutzt, der auf den Wert verweist.
Zusätzlich können Sie die Validierung von HTML_QuickForm nutzen. Die Prüfregeln, die in QuickForm definiert sind, können Sie hier auch nutzen. Welche Regeln es gibt, schlagen Sie bitte in Abschnitt 17.9 über HTML_QuickForm nach. Nutzen Sie den Schlüssel 'qf_rules', und übergeben Sie ihm ein Array mit Regeln. Bei diesem verschachtelten Array wird der Name der anzuwendenden Regel als Schlüssel genutzt. Als Wert nutzen Sie im einfachsten Fall die Meldung, die ausgegeben werden soll, wenn die Regel fehlschlägt. Benötigt die Regel zwei Werte, zum Beispiel weil die Eingabe eine bestimmte Mindestlänge haben muss, übergeben Sie auch an dieser Stelle wieder ein Array. Das erste Element ist dann die Fehlermeldung, worauf der zweite Wert folgt.
Die Prüfung der Regeln müssen Sie dann selbst auf Basis des QuickForm-Objekts implementieren, das getForm() Ihnen zurückgibt.
In Abbildung 15.3 sehen Sie ein Beispiel, in dem der Code aus Listing 15.10 um entsprechende Labels ergänzt wurde. Zur Ausgabe des Formulars wurden die folgenden Zeilen ergänzt:
$form = $tab->getForm(array('vname', 'nname','fon','kommentar')); $form->addElement('submit','','Daten speichern'); $form->display();
In diesem Fall wurde der Methode getForm() ein Array mit Spaltennamen übergeben. Dieses dient dazu festzulegen, welche Felder ausgegeben werden sollen. Wird die Methode ohne Parameter aufgerufen, wird für jede Spalte ein Formularelement eingeblendet. Auf diesem Wege können Sie übrigens auch Default-Werte für Text-Elemente übergeben, indem Sie die Spaltennamen als Schlüssel nutzen und ihnen jeweils den gewünschten Wert übergeben.
Abbildung 15.3 Das generierte Formular im Browser
Die Verarbeitung der Daten, also das Einfügen in die Tabelle, müssen Sie selbst implementieren. Dafür ist kein Automatismus vorgesehen. Die Namen der Formularelemente entsprechen dabei den Namen der Spalten in der Datenbank.
15.4.3 Auslesen von Werten 

Natürlich unterstützt das Paket auch die Möglichkeit, Daten aus der Tabelle auszulesen. Dazu ist in der Klasse eine Eigenschaft namens $sql vorzusehen. Diese bekommt ein assoziatives Array übergeben. Die Namen, die Sie nutzen, dienen dazu, die SQL-Befehle nachher ansprechen zu können. Unterhalb jedes Schlüssels ist wiederum ein Array angesiedelt, das mindestens den Schlüssel 'select' enthalten muss. Er enthält die Daten, die selektiert werden sollen, in Form eines Strings. Dieser String wird direkt in die Abfrage übernommen, so dass Sie hier die volle SQL-Syntax nutzen können.
Zusätzlich stehen noch die Schlüssel aus Tabelle 15.8 zur Verfügung. Auch dabei werden die übergebenen Strings direkt in den SQL-Befehl übernommen, wobei das 'get' eine Ausnahme darstellt.
Die eigentliche Abfrage wird dann mit der Methode select() durchgeführt. Die Methode bekommt den Namen der Abfrage übergeben, also den Schlüssel, den Sie bei der Eigenschaft $sql genutzt haben. Das heißt, wenn in der Deklaration der Klasse diese Eigenschaft definiert ist:
public $sql = array ( 'alle' => array ( 'select' => '*', 'get' => 'assoc' ), 'namen' => array ( 'select' => 'vname, nname', 'order' => 'nname DESC', 'get' => 'assoc' ) )
dann können Sie den eigentlichen SQL-Befehl mit
$erg = $tab->select ('alle');
oder
$erg = $tab->select ('namen');
ausführen. Um Ihnen mehr Flexibilität zu geben und Sie nicht zu zwingen, eine Unzahl von SQL-Statements in der Klassendefinition vorzusehen, können Sie select() noch weitere, optionale Parameter übergeben. Als zweiten Parameter können Sie eine Ergänzung zu einer schon bestehenden WHERE-Bedingung übergeben. Diese wird dann mit einem AND ergänzt. Sollte in der Abfrage selbst noch kein WHERE-Teil definiert sein, wird der hier übergebene als einziger genutzt.
Es ist möglich, als dritten Parameter einen String zu übergeben, der die Sortierung beeinflusst. Ist in dem Array, das die Abfrage definiert, schon eine Sortierung definiert, wird sie überschrieben.
Mit dem vierten und fünften Parameter können Sie definieren, wie viele Werte zurückgegeben werden. Der vierte Parameter legt dabei den Startwert fest, wobei die Zahl 0 den ersten Wert der Ergebnismenge bezeichnet. Der fünfte Parameter legt die Anzahl der Werte fest. Somit können Sie auf dieser Basis schnell ein »Blättern« durch die Werte implementieren.
Da die Methode select() im Hintergrund auf die Klasse PEAR::DB zugreift, können Sie die Art, wie die Werte zurückgegeben werden, noch beeinflussen. Wie in Tabelle 15.8 erläutert wird, können Sie 'get' verschiedene Konstanten zuweisen. Bei 'all' und 'assoc' werden die eigentlichen Werte in indizierten Arrays zurückgegeben. Weisen Sie der Eigenschaft fetchmode die Konstante DB_FETCHMODE_ASSOC zu, werden assoziative Arrays genutzt. Mit DB_FETCHMODE_OBJECT erhalten Sie ein Array mit Objekten. Die Objekte leiten sich aus PHP stdClass ab. Um eine andere Klasse zu nutzen, weisen Sie den Namen dieser Klasse der Eigenschaft fetchmode_object_class zu. Wie eine solche Klasse aufzubauen ist, können Sie auf Seite 419 nachlesen.
15.4.4 Manipulation von Daten 

Möchten Sie Daten löschen, nutzen Sie die Methode delete(). Diese bekommt als Parameter die WHERE-Bedingung übergeben, die einschränkt, welche Datensätze gelöscht werden sollen. Diese Bedingung wird als kompletter String in SQL-Syntax übergeben.
Ähnlich einfach ist es, einen bestehenden Datensatz zu verändern, was die Methode update() für Sie übernimmt. Als zweiten Parameter bekommt auch sie eine Bedingung übergeben, die mit einem WHERE kombiniert wird. Als ersten Parameter erwartet die Methode ein Array, wie es bei dem insert() Verwendung findet. Allerdings müssen Sie in diesem Zusammenhang natürlich kein Array übergeben, das alle Spalten enthält. Es reicht, die Spaltennamen zu nutzen, bei denen die Werte verändert werden sollen.