Photoshop
Cinema 4d
Fotografie
Weitere Grafiksoftware
HTML / CSS
JavaScript
Flash
PHP
Webserver
Sonstige
Pear DB_DataObject Einsteiger-Tutorial (PHP Tutorial)
Tutorial erstellt von TakaBo, letzte Änderung am 04.07.2007- 1. Ziel
- 2. Voraussetzung
- 3. Warum ORM?
- 4. Pear Crashkurs
- 5. Einstieg in DB_DataObject
- 6. Aussichten
Ziel
Ziel diese Tutorials soll es sein, dem Leser einen Einblick in das Object Relational Mapping (ORM) zu geben. Der Leser sollte danach in der Lage sein eigene kleine Projekt mit Hilfe von ORM abzubilden.
Voraussetzung
Für dieses Tutorial wird PHP benötigt. Das Tutorial selber wurde mit PHP5 getestet. Ein Funktionieren mit PHP4 kann nicht garantiert werden. Ein Webserver ist optional. Desweiteren benötigen wir ein Datenbankmanagement-System (DBMS) wie z.B. PostgreSQL oder MySQL.
Warum ORM?
Objektorientiertes Programmieren war und ist nicht nur ein „hype“ unserer Tage, sondern es verspricht auch viele Vorteile gegenüber prozeduralem Programmieren. Z.B. Verstecken der Daten vor anderen Objekten durch eine wohl definierte Schnittstelle (keine unerwartete Manipulation der Daten), auch Blackbox Prinzip genannt. Vererbung von Methoden und Daten an andere Klassen. Bildung von Kompositionen ( Mehrere Klassen werden zu Datentypen einer neuen Klasse ) usw. Zum Einen ermöglicht es die Wiederverwendbarkeit von Code zu erleichtern, zum Anderen hilft die objektorientierte Sicht beim Entwurf einer Software, da wir Mensche in einer objektorientierten Welt leben.
Doch was hat das ganze mit ORM zu tun? Ganz einfach. Irgendwo auf dem Weg von der prozeduralen Programmierung zur objektorientierten Programmierung hat man den Kontakt zu den Datenbanken verloren. Die Darstellung von Datenbeständen in Relationen ist eher was für prozedurale Programme, denn objektorientierte. Umständliche Initialisierungen und Abfragen der DBMS erschweren den Umgang mit Datenbeständen. Besonders in PHP, in der es offensichtlich bei jeder neuen Minor-Versionsnummer eine geänderte Datenbank-API zu geben scheint, wäre es sinnvoll die Datenbankschnittstellen zu abstrahieren, besser noch: Eine einheitliche objektorientierte Schnittstelle anzubieten, um persistente abstrakte Datentypen zu erzeugen. Dieser Vorgang, d.h. das Abbilden von Relationen auf abstrakte Datentypen (bzw deren Objekte), wird als Object Relational Mapping , kurz ORM, bezeichnet.
Pear Crashkurs
DB_DataObject ist ein ORM-Tool, das mit dem PHP-Tool PEAR installiert werden kann. Da PEAR selbst bei den meisten Distributionen dabei ist, erspare ich mir eine explizite Installationsanleitung zu PEAR und widme mich den Paketen von PEAR. Ein Aufruf von pear list in der Konsole gibt darüber Auskunft, ob das Paket DB_DataObject installiert ist.
dsh#pear list
Installed packages, channel pear.php.net:
=========================================
Package Version State
Archive_Tar 1.3.1 stable
Console_Getopt 1.2 stable
DB 1.7.6 stable
DB_DataObject 1.7.15 stable
Date 1.4.6 stable
PEAR 1.4.5 stable
XML_RPC 1.4.4 stable
Sollte DB_DataObject nicht in der Ausgabe auftauchen, muß es nachinstalliert werden.Ganz einfach geht das über den PEAR Befehl install. Da DB_DataObject auf die Pakete Date und DB angewiesen ist, installieren wir erst diese mit:
#pear install Date
und
#pear install DB
Während der Installation werden wir darauf hingewiesen, daß wir auch noch optionale Pakete installieren könnten. Diese sind für dieses Tutorial aber nicht notwendig.
Danach können wir mit
#pear install DB_DataObject
das ORM-Paket von PEAR installieren.
Häufig kommt es zu Problemen, weil Suse und Co das PEAR-Verzeichnis nicht in den include_path-Schlüssel der php.ini aufgenommen haben. Mit Hilfe von
#pear config-show
kann man sich die Konfiguration von PEAR genauer ansehen, die ungefähr so aussehen sollte:
CONFIGURATION (CHANNEL PEAR.PHP.NET):
=====================================
Auto-discover new Channels auto_discover <not set>
Default Channel default_channel pear.php.net
HTTP Proxy Server Address http_proxy <not set>
PEAR server [DEPRECATED] master_server pear.php.net
Default Channel Mirror preferred_mirror pear.php.net
Remote Configuration File remote_config <not set>
PEAR executables directory bin_dir /usr/local/bin
PEAR documentation directory doc_dir /usr/local/share/doc/pear
PHP extension directory ext_dir /usr/local/lib/php/20050922
PEAR directory php_dir /usr/local/share/pear
PEAR Installer cache directory cache_dir /tmp/pear/cache
PEAR data directory data_dir /usr/local/share/pear/data
PHP CLI/CGI binary php_bin /usr/local/bin/php
PEAR test directory test_dir /usr/local/share/pear/tests
Cache TimeToLive cache_ttl 3600
Preferred Package State preferred_state stable
Unix file mask umask 22
Debug Log Level verbose 1
PEAR password (for password <not set>
maintainers)
Signature Handling Program sig_bin /usr/local/bin/gpg
Signature Key Directory sig_keydir /usr/local/etc/pearkeys
Signature Key Id sig_keyid <not set>
Package Signature Type sig_type gpg
PEAR username (for username <not set>
maintainers)
User Configuration File Filename /root/.pearrc
System Configuration File Filename /usr/local/etc/pear.conf
Besonders wichtig ist hierbei die Zeile PEAR directory, da hier der Pfad zum PEAR-Verzeichnis angegeben wird. In der Datei php.ini muß der Pfad im Schlüssel include_path eingetragen werden, in diesem Fall also include_path=“.:/usr/local/share/pear“. Damit ist gewährleistet, daß PHP die PEAR-Pakete später auch findet. Ein weiterer Fallstrick bei SUSE u.a. ist die Tatsache, daß es für php-cli und mod_php getrennte php.ini Dateien gibt. Das kann dazu führen, daß ein php-Programm in der Konsole funktioniert, aber nicht im Apache als mod_php.
Einstieg in DB_DataObject
DB_DataObject basiert auf DB, dem Datenbankabstraktions-Layer von PEAR. Ich will hier nicht näher darauf eingehen. Es sei nur so viel gesagt: Es ermöglicht einen einheitlichen Zugriff auf Datenbank Management Systeme. So brauch man sich fast keine Gedanken darum zu machen, welches DBMS als Backend läuft, da die API Einheitlich ist. Lediglich der Initialisierungsstring ( auch DSN genannt ) entscheidet über das DBMS. Wen es interessiert, dem sei die Dokumentation auf http://pear.php.net ans Herz gelegt.
Als Beispiel wollen wir eine Datentabelle für Modellautos anlegen. Wir überlegen uns dazu ein mögliches Schema:
Code:
tabellenname=matchbox
artikelnummer:varchar(10); //Primärer Schlüssel, Bedingung nicht NULL, Eindeutig
bezeichnung:varchar(30);
preis:float;
artikelnummer:varchar(10); //Primärer Schlüssel, Bedingung nicht NULL, Eindeutig
bezeichnung:varchar(30);
preis:float;
Unter PostgreSQL könnte die Datentabelle mit SQL so aufgebaut werden:
Code:
CREATE TABLE matchbox
(
artikelnummer varchar(10) NOT NULL,
bezeichnung varchar(30),
preis float4,
PRIMARY KEY (artikelnummer),
) WITHOUT OIDS
TABLESPACE pg_default;
ALTER TABLE matchbox OWNER TO pgsql;
(
artikelnummer varchar(10) NOT NULL,
bezeichnung varchar(30),
preis float4,
PRIMARY KEY (artikelnummer),
) WITHOUT OIDS
TABLESPACE pg_default;
ALTER TABLE matchbox OWNER TO pgsql;
Für MySQL muß das ganze entsprechend angepasst werden. Nun füllen wir die Tabelle mal mit ein paar Datensätzen.
Code:
INSERT INTO matchbox (artikelnummer, bezeichnung, preis) VALUES ('G5550-42', 'Pontiac® Solstice Concept', 12.95);
INSERT INTO matchbox (artikelnummer, bezeichnung, preis) VALUES ('G5550-72', 'Lamborghini Diablo', 15.99);
INSERT INTO matchbox (artikelnummer, bezeichnung, preis) VALUES ('G5550-28', '1970 Mustang Boss 302', 17.95);
INSERT INTO matchbox (artikelnummer, bezeichnung, preis) VALUES ('G5550-72', 'Lamborghini Diablo', 15.99);
INSERT INTO matchbox (artikelnummer, bezeichnung, preis) VALUES ('G5550-28', '1970 Mustang Boss 302', 17.95);
Was wir nun haben, ist eine Datenbanktabelle mit ein paar Datensätzen. Und? Das Beste kommt jetzt ;-)
Wir konfigurieren uns das DB_DataObject Paket. Wir benutzen dazu eine INI-Datei, wie sie z.B. auch unter Windows Anwendung findet. In dieser INI-Datei können auch andere Parameter für die Anwendung stehen. Wir beschränken uns hier erstmal nur auf die für DB_DataObject relevanten Schlüssel.
Wir erstellen die Datei system.ini in unserem Arbeitsverzeichnis mit folgendem Inhalt:
Code:
[DB_DataObject]
database = pgsql://pgsql:@localhost/tutorial
schema_location = /home/work/tutorial/DataObjects
class_location = /home/work/tutorial/DataObjects
class_prefix = DO_
generator_strip_schema = 1
database = pgsql://pgsql:@localhost/tutorial
schema_location = /home/work/tutorial/DataObjects
class_location = /home/work/tutorial/DataObjects
class_prefix = DO_
generator_strip_schema = 1
Der erste Eintrag in eckigen Klammern gibt an, dass alle Schlüssel danach für das DB_DataObject-Paket gedacht ist. Der Schlüssel database enthält die so genannte DSN (Data Source Name). Sie hat folgenden Aufbau:
<DBMS-Namenskürzel>://<Benutzer>:<Passwort>@<host>/<datenbank>
In unserem Beispiel haben wir das <DBMS-Namenskürzel> mit pgsql belegt. Mögliche andere Werte sind, wenn die entsprechende Extension für PHP mit eingebunden sind:
dbase -> dBase
fbsql -> FrontBase
ibase -> InterBase
ifx -> Informix
msql -> Mini SQL
mssql -> Microsoft SQL Server
mysql -> MySQL (für MySQL <= 4.0)
mysqli -> MySQL (für MySQL >= 4.1) (benötigt PHP 5)
oci8 -> Oracle 7/8/9
odbc -> ODBC (Open Database Connectivity)
pgsql -> PostgreSQL
sqlite -> SQLite
sybase -> Sybase
(Quelle: http://pear.php.net)
Der <Benutzer> ist in diesem Beispiel pgsql (Default unter FreeBSD bei Verwendung von PostgreSQL, muß für das entsprechende DBMS angepasst werden) mit einem leeren Passwort.
Die Schlüssel schema_location und class_location zeigen DB_DataObject den Ort an, wo es Informationen zu den Datenbanktabellen speichern kann.
Der Schlüssel class_prefix gibt an, welchen Namenszusatz DB_DataObject vor die erzeugten Klassen setzten soll.
generator_strip_schema=1 ist u.a. für PostgreSQL gedacht, um z.B. das Schema „public“ aus dem Namen zu entfernen. Der Sinn ist, daß man nachher die Objekte gleich benamen kann, wie die Datentabelle, also „matchbox“ anstatt „public_matchbox“.
Unsere Verzeichnis-Struktur sollte in etwa so aussehen:
/home/work/tutorial
|-/DataObjects
|-system.ini
Jetzt wird createTables.php aufrufen, das sich im Verzeichnis DB/DataObject unterhalb des PEAR-Pfades bedindet. Der Aufruf sollte in etwa folgendes Ergebnis liefern:
dsh# php /usr/local/share/pear/DB/DataObject/createTables.php system.ini
DB_DataObject_Generator : 0 : CREATING FOR tutorial
DB_DataObject_Generator : 0 : calling generateDefinitions
DB_DataObject_Generator : 0 : Generating Definitions file:
DB_DataObject_Generator : 0 : Writing ini as /home/work/tutorial/DataObjects/tutorial.ini
DB_DataObject_Generator : 0 : calling generateClasses
DB_DataObject_Generator : 0 : writing DO_matchbox
DB_DataObject_Generator : 0 : DONE
dsh#
Im Verzeichnis DataObjects befinden sich zwei neue Dateien :
Matchbox.php und tutorial.ini.
Unser erstes Programm
Wir machen uns das ORM jetzt in einem ersten einfachen Programm zu nutzen. Es soll uns den Namen und den Preis des Modells für eine Artikelnummer zurück geben.
Zuerst schreiben wir uns ein Modul, daß die Konfiguration von DB_DataObject für unsere Projekte vor nimmt.
Code:
<?php
// Dateiname:dbo_config.php
require_once 'DB/DataObject.php';
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$config = parse_ini_file(dirname(__FILE__).'/system.ini',TRUE);
$options = $config['DB_DataObject'];
?>
// Dateiname:dbo_config.php
require_once 'DB/DataObject.php';
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$config = parse_ini_file(dirname(__FILE__).'/system.ini',TRUE);
$options = $config['DB_DataObject'];
?>
Um nun das eigentliche Programm:
Code:
<?php
// Dateiname: gib.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->get('G5550-42');
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."n";
?>
// Dateiname: gib.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->get('G5550-42');
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."n";
?>
Die generierte Ausgabe sieht in etwa so aus:
dsh#php gib.php
Art. Nr:G5550-42
Bez. :Pontiac® Solstice Concept
Preis :12.95
Dieses Beispiel nutzt die Möglichkeit über die Methode get(key) direkt den primären Schlüssel abzufragen. Aber man kann auch eine andere Lösung benutzen:
Code:
<?php
// Dateiname: gib2.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->preis=15.99;
$num=$car->find();
if ($num)
{
$car->fetch();
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."n";
}
else
{
echo "Kein Ergebnis gefunden.";
}
?>
// Dateiname: gib2.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->preis=15.99;
$num=$car->find();
if ($num)
{
$car->fetch();
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."n";
}
else
{
echo "Kein Ergebnis gefunden.";
}
?>
Die Ausgabe dazu sollte das Ergebnis liefern:
dsh#php gib2.php
Art. Nr:G5550-72
Bez. :Lamborghini Diablo
Preis :15.99
Es wird ersichtlich, wie einfach Datenbankabfragen gestalltet werden können. Auch Iterationen sind kein Problem mehr, bei denen man unleserliche abenteuerliche Konstrukte entwerfen muß:
Code:
<?php
//Dateiname: gib3.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->whereAdd('preis > 15.00');
$num=$car->find();
if ($num){
while($car->fetch()){
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."nn";
}
}
else
{
echo "Kein Ergebnis gefunden!";
}
?>
//Dateiname: gib3.php
require_once dirname(__FILE__).'/dbo_config.php';
$car=DB_DataObject::factory('matchbox');
$car->whereAdd('preis > 15.00');
$num=$car->find();
if ($num){
while($car->fetch()){
echo "Art. Nr:".$car->artikelnummer."n";
echo "Bez. :".$car->bezeichnung."n";
echo "Preis :".$car->preis."nn";
}
}
else
{
echo "Kein Ergebnis gefunden!";
}
?>
Auch hier das Ergebnis nach Aufruf von php gib3.php:
dsh#php gib3.php
Art. Nr:G5550-72
Bez. :Lamborghini Diablo
Preis :15.99
Art. Nr:G5550-28
Bez. :1970 Mustang Boss 302
Preis :17.95
Aber auch Einfügen und Löschen von Datensätzen stellt für DB_DataObject kein Problem dar.
Code:
<?php
// Dateiname: setze.php
require_once dirname(__FILE__).'/dbo_config.php';
function suche()
{
echo "Suche...n";
$newcar=DB_DataObject::factory('matchbox');
$newcar->whereAdd('preis < 1.0');
$num=$newcar->find();
if ($num)
{
$newcar->fetch();
echo "Art. Nr:".$newcar->artikelnummer."n";
echo "Bez. :".$newcar->bezeichnung."n";
echo "Preis :".$newcar->preis."nn";
}
else
{
echo "Keinen Datensatz gefunden!n";
}
}
$car=DB_DataObject::factory('matchbox');
echo "Objekt anlegenn";
$car->artikelnummer='B7772-33';
$car->bezeichnung='VW-Kaefer';
$car->preis=0.99;
$car->insert();
// Suchen
suche(); //Sollte ein Ergebnis zurück liefern
echo "Objekt wieder löschenn";
$car->delete();
// Suchen
suche(); //Sollte kein Ergebnis mehr liefern
?>
// Dateiname: setze.php
require_once dirname(__FILE__).'/dbo_config.php';
function suche()
{
echo "Suche...n";
$newcar=DB_DataObject::factory('matchbox');
$newcar->whereAdd('preis < 1.0');
$num=$newcar->find();
if ($num)
{
$newcar->fetch();
echo "Art. Nr:".$newcar->artikelnummer."n";
echo "Bez. :".$newcar->bezeichnung."n";
echo "Preis :".$newcar->preis."nn";
}
else
{
echo "Keinen Datensatz gefunden!n";
}
}
$car=DB_DataObject::factory('matchbox');
echo "Objekt anlegenn";
$car->artikelnummer='B7772-33';
$car->bezeichnung='VW-Kaefer';
$car->preis=0.99;
$car->insert();
// Suchen
suche(); //Sollte ein Ergebnis zurück liefern
echo "Objekt wieder löschenn";
$car->delete();
// Suchen
suche(); //Sollte kein Ergebnis mehr liefern
?>
Die Ausgabe sieht dann etwa so aus:
dsh#php setze.php
Objekt anlegen
Suche...
Art. Nr:B7772-33
Bez. :VW-Kaefer
Preis :0.99
Objekt wieder löschen
Suche...
Keinen Datensatz gefunden!
Aussichten
Für den Einstieg sollte das erstmal reichen. Wer neugierig geworden ist, der kann sich die Dokumentation zu DB_DataObject ansehen unter http://pear.php.net/manual/en/package.database.db-dataobject.php.
Besonders interessant wird die Benutzung von DB_DataObject mit dem FormBuilder, der es einem ermöglicht auf relativ einfache Art und Weise Formulare aus Datensätzen aufzubauen, Formulardaten zu Validieren und Daten aus Formularen in Datentabellen abzuspeichern. Da ich selbst intensiv dabei bin in die Materie vorzudringen, könnte zu dem Thema vielleicht noch ein Tutorial herausspringen.
Für Verbesserungsvorschläge und Anmerkungen bin ich hier im Forum erreichbar unter takabo.
Gruß Chris
>> Allgemeine Fragen oder Probleme mit dem Tutorial? Hier gehts zum Forum!