>> Inhaltsverzeichnis >> Anleitung für Entwickler >> Kochbuch

Anleitung für Entwickler

Kochbuch

Datenbanken

Allgemeines zum Verständnis

Welche Features werden unterstützt?

Das Yana-Framework bietet eine API zum Arbeiten mit Datenbanken, welche auf PEAR-DB basiert. Diese API erweitert die Fähigkeiten von PEAR um folgende Features:

Welche Features werden nicht unterstützt?

Es gibt einige Features welche die FlatFile-Datenbank zur Zeit nicht unterstützt, die aber für eine zukünftige Version geplant sind.

Es gibt einige Features welche in Datenbankschemata zur Zeit nicht unterstützt werden, aber für zukünftige Versionen geplant sind.

Wie erstelle ich eine Verbindung zu einer Datenbank?

Voraussetzung ist eine Schemadatei. Die Schemadateien müssen sich im Verzeichnis "config/db/" befinden und die Dateiendung ".config" besitzen.

Um eine Datenbankverbindung mit den aktuellen Verbindungsdaten herzustellen:
<?php 
    global $YANA;
    $name_der_strukturdatei "guestbook";
    $datenbankverbdinung $YANA->connect($name_der_strukturdatei);
?>

Die Verbindungsdaten für die Datenbank (wie Hostadresse, Nutzername und Passwort) werden vom Nutzer im Administrationsmenü eingegeben. Sie müssen diese im Quellcode NICHT angeben.

Wenn Sie keine Strukturdatei verwenden möchten, können Sie diese Angabe frei lassen. Dies wird jedoch in einer Produktionsumgebung nicht empfohlen. In diesem Fall wird das Framework versuchen, die Angaben selbst zu ermitteln. Falls dies fehlschlägt ist die Datenbankverbindung nicht nutzbar.

Um eine Datenbankverbindung mit individuellen Verbindungsdaten herzustellen:

Wenn Sie eine Verbindung mit einer Datenbank herstellen, aber die Verbindungsdaten selbst angeben wollen, dann gehen Sie wie folgt vor:

<?php 
/* Die Verbindungsdaten werden als assoziatives Array angegeben: */
$verbindungsdaten = array(
    'DBMS' => 'mysql',
    'HOST' => 'localhost',
    'PORT' => 0,
    'USERNAME' => 'nutzername',
    'PASSWORD' => 'passwort',
    'DATABASE' => 'name_der_datenbank'
);

/* Um die YANA-API zur Kommunikation mit der Datenbank zu benutzen schreiben Sie: */

$datenbank_server = new DbServer($verbindungsdaten);
$yana_api = new DbStream($datenbank_server);

/* Um die PEAR-API zur Kommunikation mit der Datenbank zu benutzen schreiben Sie: */

$datenbank_server = new DbServer($verbindungsdaten);
$pear_api $datenbank_server->get();

/* Um die PEAR-API mit den Standardverbindungsdaten zu benutzen schreiben Sie: */

$datenbank_server = new DbServer();
$pear_api $datenbank_server->get();
?>

Wie prüfe ich, ob im Administrationsmenü die Datenbankverbindung aktiviert ist?

<?php 
    if (YANA_DATABASE_ACTIVE === true) {
    print "Datenbank ist aktiviert";
    } else if (YANA_DATABASE_ACTIVE === false) {
    print "Datenbank ist NICHT aktiviert";
    }
?>

Wie erstelle ich eine Datenbankabfrage?

Dazu bietet die API die Funktion $db->get(string $key). Diese führt eine Select-Anfrage auf der Datenbank aus und liefert den Wert an der Stelle zurück, welche mit dem Argument $key angegeben wurde.

<?php 
    global $YANA;
    $db $YANA->connect("guestbook");
    
    /*
   Es gilt folgende Syntax:
    
   $db->get(
            string "$tabelle.$zeile.$spalte",
            string $where,
            string $order_by,
            int $offset,
            int $limit
           );
    
     Beispiel:
   $value = $db->get("table.1.field","row1=wert1,row2=wert2","order by row1",0,1);
    
   erzeugt folgende SQL-Anfrage:
   SELECT field from table where primary_key = "1" and row1 like '%wert1%' and row2 like '%wert2%' order by row1 limit 1;
    */
    
    /* Feld ausgeben */
    $value $db->get("table.1.field");
    /*
   erzeugt folgende SQL-Anfrage:
   SELECT field from table where primary_key = "1";
    */
    
    /* Spalte ausgeben: */
        $column $db->get("table.*.field");
    foreach ($column as $row => $value)
    {
        print "<p>Value of 'field' in row '$row' = $value</p>";
    }
    /*
   erzeugt folgende SQL-Anfrage:
   SELECT field from table;
    */
    
    /* Zeile ausgeben: */
    $row $db->get("table.2");
    foreach ($row as $column => $value)
    {
        echo "<p>Value of column '$column' in row '2' = $value</p>";
    }
    /*
   erzeugt folgende SQL-Anfrage:
   SELECT * from table where primary_key = "2";
    */
    
    /* Tabelle ausgeben: */
    $table $db->get("table");
    foreach ($table as $index => $row)
    {
        foreach ($row as $column => $value)
        {
            echo "<p>Value at 'table.$index.$column' = $value</p>";
        }
    }
    /*
   erzeugt folgende SQL-Anfrage:
   SELECT * from table;
    */
?>

Wie führe ich INSERT- / UPDATE-Statements aus?

Dazu verwenden Sie die Funktion $db->insert($key,$value). Diese fügt den Wert „value“ an der Stelle „key“ ein. Dabei kann es sich entweder um eine Zeile oder eine Tabellenzelle handeln. Das Einfügen von ganzen Tabellen oder Spalten ist nicht möglich.

Beim ersten Aufruf der Funktion wird automatisch eine Transaktion gestartet. Benutzen Sie die Funktion $db->write() um ein COMMIT der Daten auszulösen. Wenn eine der Anweisungen in der Transaktion fehlschlägt, wird automatisch ein CALLBACK ausgeführt.

Falls die Zeile nicht existiert, wird ein die SQL-Anweisung „insert“ benutzt, sonst „update“.

Die Funktion gibt bei Erfolg „true“ zurück und „false“ sonst.

Bitte beachten Sie: die SQL-Anweisung wird erst ausgeführt, wenn die Funktion $db->write() aufgerufen wird.

Im Folgenden einige Beispiele:

<?php 
global $YANA;$db $YANA->connect("guestbook");

/* <b>Neue Zeile einfügen:</b> */
$db->insert("table.*",array("row1"=>"wert1","row2"=>"wert2"));
$db->write();

/* <b>Zeile aktualisieren:</b> */
$db->insert("table.2",array("row1"=>"wert1","row2"=>"wert2"));
$db->write();

/* <b>Zelle aktualisieren:</b> */
$db->insert("table.2.row1","wert1");
$db->write();

/* <b>Transaktion durchführen:</b> */
$db->insert("table.*",array("row1"=>"wert1","row2"=>"wert2"));
$db->insert("table.*",array("row1"=>"wert3","row2"=>"wert4"));
$db->insert("table.1.row3","wert1");
$db->write();
?>

Wie führe ich DELETE-Statement aus?

Dazu verwenden Sie die Funktion $db->remove($key). Diese löscht den Datensatz an der Adresse „key“ aus der Tabelle. Die Funktion gibt bei Erfolg „true“ zurück und „false“ sonst. Es können nur Datensätze gelöscht werden. Keine Tabellen, Zellen oder Spalten.

Beachten Sie folgende Beschränkung: aus Sicherheitsgründen wird pro Aufruf stets maximal 1 Datensatz gelöscht. Wenn Sie mehrere Datensätze löschen wollen, müssen Sie die Funktion mehrmals aufrufen. Diese Einschränkung soll verhindern, dass jemand durch Unachtsamkeit oder aus einem Versehen eine gesamte Tabelle löschen kann.

Bitte beachten Sie: die SQL-Anweisung wird erst ausgeführt, wenn die Funktion $db->write() aufgerufen wird.

<?php 
global $YANA;$db $YANA->connect("guestbook");

/* <b>Die zweite Zeile löschen:</b> */
$db->remove("table.2");
$db->write();
/*
 * <b>erzeugt folgende SQL-Anfrage:</b>
 * DELETE FROM table WHERE primary_key = "2" LIMIT 1;
 */

/* <b>Die ganze Tabelle "table" löschen:</b> */
for ($i=0$i $db->length($table); $i++)
{
    $db->remove("table.*");
}
$db->write();
/*
 * <b>erzeugt folgende SQL-Anfrage:</b>
 * DELETE FROM table WHERE primary_key = "2" LIMIT 1;
 */
?>

Wie finde ich heraus wie viele Datensätze eine Tabelle hat?

<?php 
global $YANA;$db $YANA->connect("guestbook");

if ($db->length("table") === 0) {
    print "Die Tabelle 'table' ist leer.";
} else {
    print "Die Tabelle 'table' enthält ".$db->length("table")." Datensätze.";
}
?>

Wie finde ich heraus ob eine Tabelle / Datensatz existiert?

<?php 
global $YANA;$db $YANA->connect("guestbook");

/* <b>Datenbankverbindung prüfen:</b> */
if ($db->exists() === true) {
    print "Die Datenbankverbindung ist verfügbar.";
} else if ($db->exists() === false) {
    print "Die Datenbankverbindung ist NICHT verfügbar";
}

/* <b>Prüfen ob Tabelle existiert:</b> */
if ($db->exists("table") === true) {
    print "Die Tabelle 'table' existiert.";
} else if ($db->exists("table") === false) {
    print "Es gibt keine Tabelle mit dem Namen 'table'.";
}

/* <b>Prüfen ob Datensatz existiert:</b> */
if ($db->exists("table.2") === true) {
    print "Der Datensatz '2' in der Tabelle 'table' existiert.";
} else if ($db->exists("table.2") === false) {
    print "Es gibt keinen Datensatz '2' in der Tabelle 'table'.";
}

/* <b>Prüfen ob Feld existiert und einen Wert hat:</b> */
if ($db->exists("table.2.field") === true) {
    print "Das Feld 'field' im Datensatz '2' in Tabelle 'table' hat einen Wert.";
} else if ($db->exists("table.2.field") === false) {
    print "Das Feld 'field' im Datensatz '2' in Tabelle 'table' existiert nicht oder ist NULL.";
}

/* <b>Prüfen ob mindestens 1 Feld existiert, dass NOT NULL ist:</b> */
if ($db->exists("table.*.field") === true) {
    print "Die Spalte 'field' in Tabelle 'table' existiert.";
} else if ($db->exists("table.*.field") === false) {
    print "Die Spalte 'field' in Tabelle 'table' existiert nicht oder alle Werte sind NULL.";
}
?>

Wie erstelle ich eine Installationsroutine für meine Tabellen?

YANA hat eine Installationsroutine für Datenbanken, welche Sie im Administrationsmenü, in der Basiskonfiguration im Menü "Datenbank Setup" finden. Über dieses Menü kann ein Nutzer alle Tabllen installieren oder Inhalte zwischen dem DBMS und der FlatFile-Datenbank synchronisieren.

Sie können diese Installationsroutine mit eigenen Einträgen "bestücken". Kopieren Sie dazu Ihr Datenbankschema in das Verzeichnis "config/db/". Die SQL-Dateien mit den erforderlichen DDL-Statements für jedes DBMS, welches Sie unterstützen wollen, speichern Sie im Verzeichnis "config/db/.install/". Dort finden Sie verschiedene Unterverzeichnisse für verschiedene Datenbankhersteller, in welchen Sie Ihre Installationsdateien ablegen können. Sie finden dort außerdem eine Datei "readme.txt", welche eine Liste der unterstützten DBMS und den Namen der Verzeichnisse enthält.

Selbstverständlich sollten Sie Ihre SQL-Dateien stets mit dem jeweiligen DBMS testen, bevor Sie diese veröffentlichen ;-)

Für zukünftige Versionen ist geplant, die erforderlichen DDL-Statements direkt aus dem Schema der Datenbank zu generieren. Die SQL-Dateien wären dann nur noch eine optionale Ergänzung.

Wie importiere ich eine SQL-Datei mit DDL-Anweisungen?

<?php 
global $YANA;$db $YANA->connect("guestbook");

$db->importSQL('data.sql');
?>

Wie exportiere ich Daten in eine CSV-Datei?

<?php 
global $YANA;$db $YANA->connect("guestbook");

$csv $db->toString("table");
file_put_contents("table.csv"$csv);
?>

Wie exportiere ich ein Datenbankschema in eine Datei?

<?php 
global $YANA;$db $YANA->connect("guestbook");

$db->exportStructure("guestbook.config");
?>

Wie schreibe ich ein eigenes Datenbankschema?

Schema-Dateien haben die Endung "*.config" und werden im Verzeichnis "config/db/" gespeichert. Eine Verbindung auf Grundlage einer Schema-Datei wird hergestellt über den PHP-Code: $YANA->connect("Name der Datei ohne Dateiendung");

Beachten Sie, dass für die Editoren "ConTEXT" und "PSPad" Code-Templates zur Verfügung stehen, welche Ihnen gerade beim Erzeugen von Datenbankschemata viel Schreibarbeit abnehmen können. Sollten Sie einen dieser beiden Editoren verwenden, installieren Sie nach Möglichkeit zunächst diese Templates bevor Sie fortfahren.

Das folgende Listing zeigt ein Schema mit allen Elementen und alle gültigen Belegungen. Existieren mehrere mögliche Varianten, dann sind diese die verschiedenen Möglichkeiten durch eine Pipe '|' voneinander getrennt. Bezeichner, die frei gewählt werden können, sind im Text fett hervorgehoben.

/* Das Feld "USE_STRICT" legt fest, ob Queries zur Laufzeit
 * gegen das Schema validiert werden oder nicht.
 */
<USE_STRICT>true|false</USE_STRICT>

/* Das Feld "READONLY" ist optional. Default = false
 */
<READONLY>true|false</READONLY>

/* Das Feld "INCLUDE" hat die gleiche Bedeutung wie der gleichnamige
 * Befehl in PHP. Es importiert eine oder mehrere Datenbankschemata in die
 * aktuelle Datei.
 * Der Dateiname wird ohne Dateiendung angegeben.
 * Also zum Beispiel: <INCLUDE>user</INCLUDE> um die Datenbank "user" zu
 * importieren, die in der Datei "config/db/user.config" beschrieben wird.
 */
<INCLUDE>Datenbankschema</INCLUDE>

/* Um mehrere Dateien zu importieren verwenden Sie folgende Schreibweise:
 */
<INCLUDE>
	<0>1.Schema</0>
	<1>2.Schema</1>
	<2>3.Schema</2>
</INCLUDE>

/* Constraints sind boolsche Ausdrücke in PHP-Syntax.
 * Sie können mit einer bestimmten SQL-Aktion verknüpft werden.
 * Ergibt der Ausdruck "false" wird die entsprechende Query nicht
 * abgeschickt und ein Log-Eintrag geschrieben. Andernfalls wird die
 * Aktion fortgesetzt.
 *
 * In Constraints können Sie keine Funktionen aufrufen, mit einer einzigen
 * Ausnahme: preg_match();
 * Außerdem haben Sie Zugriff auf folgende Konstanten:
 * $VALUE      = (für INSERT, UPDATE) Wert der eingefügt wird
 * $PERMISSION = Zugriffslevel des Nutzers, der die Aktion ausgelöst hat
 * $OPERATION  = SQL-Kommando das gerade durchgeführt wird (SELECT, INSERT, ...)
 * $TABLE      = Name der Zieltabelle (meist die aktuelle Tabelle)
 * $FIELD      = Name der Zielspalte (falls angegeben)
 * $ID         = Id des aktuellen Profils (seit Version 2.8.9)
 *
 * Beispiele:
 * <SELECT>true</SELECT>
 * <UPDATE>false</UPDATE>
 * <UPDATE>$VALUE > 0 && $VALUE < 500</UPDATE>
 * <INSERT>$PERMISSION > 50</INSERT>
 * <INSERT>preg_match('/^[\w\d-_]*$/i', $VALUE)</INSERT>
 *
 * Constraints gibt es in YANA seit Version 2.8 .
 */
<CONSTRAINT>
	<SELECT>PHP-Code</SELECT>
	<INSERT>PHP-Code</INSERT>
	<UPDATE>PHP-Code</UPDATE>
	<DELETE>PHP-Code</DELETE>
</CONSTRAINT>

/* Trigger sind Miniaturprogramme in PHP-Syntax.
 * Sie werden an eine bestimmte SQL-Aktion geknüpft und automatisch ausgeführt bevor
 * oder nachdem das SQL Statement ausgeführt wurde.
 *
 * DIESE Trigger unterscheiden sich von Triggern direkt in einer Datenbank.
 * 1) PHP kann verwendet werden,
 * 2) unabhängig vom DBMS, aber:
 * 3) kein direkter Zugriff auf die Datenbank.
 *
 * Diese Trigger eignen sich vor allem für Logging und Modifizieren von Eingabedaten.
 *
 * Außerdem haben Sie Zugriff auf folgende Konstanten:
 * $VALUE      = (für INSERT, UPDATE) Wert der eingefügt wird
 * $PERMISSION = Zugriffslevel des Nutzers, der die Aktion ausgelöst hat
 * $OPERATION  = SQL-Kommando (BEFORE_INSERT, AFTER_UPDATE, ...)
 * $TABLE      = Name der Zieltabelle (meist die aktuelle Tabelle)
 * $FIELD      = Name der Zielspalte (falls angegeben)
 * $ID         = Id des aktuellen Profils (seit Version 2.8.9)
 *
 * Beispiele:
 * <BEFORE_INSERT>$VALUE = md5($VALUE);true</BEFORE_INSERT>
 * <AFTER_DELETE>if($VALUE){print 'Erfolgreich gelöscht.';}else{print 'Fehler.';}</AFTER_DELETE>
 */
<TRIGGER>
	<BEFORE_INSERT>PHP-Code</BEFORE_INSERT>
	<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
	<BEFORE_DELETE>PHP-Code</BEFORE_DELETE>
	<AFTER_INSERT>PHP-Code</AFTER_INSERT>
	<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
	<AFTER_DELETE>PHP-Code</AFTER_DELETE>
</TRIGGER>

/* Wie Sie vielleicht schon vermutet haben: die Option READONLY=true
 * und der Constraint UPDATE=false haben beide den gleichen Effekt.
 */

/* Hier folgt die Definition der Tabellen.
 */
<TABLES>
	<Name der Tabelle>
		<READONLY>true|false</READONLY>

		<CONSTRAINT>
			<SELECT>PHP-Code</SELECT>
			<INSERT>PHP-Code</INSERT>
			<UPDATE>PHP-Code</UPDATE>
			<DELETE>PHP-Code</DELETE>
		</CONSTRAINT>
		<TRIGGER>
			<BEFORE_INSERT>PHP-Code</BEFORE_INSERT>
			<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
			<BEFORE_DELETE>PHP-Code</BEFORE_DELETE>
			<AFTER_INSERT>PHP-Code</AFTER_INSERT>
			<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
			<AFTER_DELETE>PHP-Code</AFTER_DELETE>
		</TRIGGER>

/* Im Feld "PRIMARY_KEY" ist der Name der Spalte anzugeben, welche den Primärschlüssel enthält.
   (in Fachliteratur siehe unter dem Stichwort: "primary key constraint")
 */
		<PRIMARY_KEY>Name der Spalte</PRIMARY_KEY>

/* Im Feld "PROFILE_KEY" kann der Name einer Spalte angegeben werden, welche den Profilschlüssel enthält.

   Sinn und Zweck:
   Sie können die Datensätze einer Tabelle einem Profil zuordnen.
   Wenn Sie das tun, sieht ein Nutzer jeweils nur die Datensätze des gerade ausgewählten Profils.

   Erzeugen eines "profile key constraint":
   1) Fügen Sie der Tabelle eine Spalte vom Typ "profile" hinzu
   2) Setzen Sie die Eigenschaft "required" dieser Spalte auf "AUTO"
   3) Setzen Sie die Eigenschaft "profile_key" der Tabelle auf den Namen der Spalte

   (Dies ist ein Spezialfall eines "compound primary key" - also eines "primary key constraint".
   Ein "Spezialfall" deshalb, weil der Schlüssel ein virtueller Schlüssel ist.
   Das heißt, dass die technische Handhabung für den Nutzer vollständig transparent geschieht.
   Sie können diesen Constraint nachträglich entfernen und der Primärschlüssel bleibt dennoch
   gültig. Das bedeutet auch, dass Sie diesen Constraint nachträglich hinzufügen oder entfernen
   können, ohne etwas am Quellcode Ihres Plugins ändern zu müssen. Prüfung und Auflösung der
   Profilschlüssel geschieht auf Ebene des DB-Layer. Dies bedeutet auch, dass es aus Sicht
   der Sicherheit Ihrer Webanwendung praktisch unmöglich ist, einen "profile key" constraint
   zu umgeben. Dieses Feature wurde eingeführt in Version 2.9.)
 */
		<PROFILE_KEY>Name der Spalte</PROFILE_KEY>

/* Im Feld "FOREIGN_KEYS" kann eine Liste von Fremdschlüsseln angegeben werden.
   (in Fachliteratur siehe unter dem Stichwort: "foreign key constraint")
 */
		<FOREIGN_KEYS>
			<Name der Spalte>Name der Zieltabelle</Name der Spalte>
			<andere Spalte>andere Zieltabelle</andere Spalte>
		</FOREIGN_KEYS>

/* Hier folgt die Definition der Tabellenspalten.
 */
		<CONTENT>
			<Name der Spalte>
				<READONLY>true|false</READONLY>

				<CONSTRAINT>
					<SELECT>PHP-Code</SELECT>
					<UPDATE>PHP-Code</UPDATE>
				</CONSTRAINT>
				<TRIGGER>
					<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
					<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
				</TRIGGER>

/* Im Feld "DESCRIPTION" kann ein Beschriftung für diese Spalte angegeben werden.
 * Sie können hier auch %TOKEN% verwenden, welche Sie zum Beispiel in Ihrem Programm,
 * oder als Sprachdatei festlegen, um eine Beschriftung in mehreren Sprachen anbieten
 * zu können.
 */
				<DESCRIPTION>Label der Spalte</DESCRIPTION>

/* Die primitiven, skalaren Datentypen integer, float und string entsprechen ihrem
 * Äquivalent in PHP. Zusätzlich wurden weitere nützliche Datentypen eingeführt.
 *
 * mail    = prüft beim Eintragen automatisch, ob der Wert eine gültige Mailadresse ist.
 * ip      = prüft beim Eintragen automatisch, ob der Wert eine gültige IP-Adresse ist.
 * text    = für Eingaben aus Textarea-Feldern, führt automatisch zusätzliche Prüfungen
 *           zum Schutz vor Flooding durch.
 * select  = ein Aufzählungsdatentyp, dessen Elemente im Feld "DEFAULT" definiert werden,
 *           siehe unten.
 * array   = kann verwendet werden, um PHP-Arrays zu speichern. Diese werden beim Auslesen
 *           des Wertes aus der Datenbank automatisch wieder umgewandelt.
 * image   = wird verwendet um Grafiken zu speichern. Bearbeitet Dateiuploads, die
 *           Prüfung und Konvertierung der Grafik und generiert automatisch Thumbnails.
 * file    = dieser Typ dient dem Speichern von Dateien ("binary large objects").
 *           Die Dateien selbst verbleiben nach dem Upload aus Gründen der besseren Performance
 *           im Dateisystem. Um Speicherplatz zu sparen, werden Sie automatich GZip-komprimiert.
 *           In der Datenbank wird lediglich der Dateiname gespeichert. Beim Abruf der Spalte
 *           wird der Name und Pfad der komprimierten Datei als Ergebnis zurückgeliefert.
 */
				<TYPE>integer|float|string|text|url|ip|mail|time|select|array</TYPE>

				<LENGTH>positive integer</LENGTH>

/* Das Feld "REQUIRED" legt fest ob ein Feld NULLABLE ist oder nicht.
 */
				<REQUIRED>true|false</REQUIRED>

/* Das Feld "DEFAULT" legt einen Wert fest, der automatisch verwendet wird, wenn beim
 * Anlegen des Datensatzes keine anderen Angaben gemacht werden.
 */
				<DEFAULT>ein Defaultwert</DEFAULT>

/* Das Feld "UNIQUE" kann entweder den Wert true, oder false annehmen, wobei false der
 * Defaultwert ist. Ist UNIQUE=true so darf kein Wert in dieser Spalte mehr als 1 Mal
 * vorkommen. Das bedeutet, jeder Wert ist eindeutig (eng. "unique").
 * Man verwendet diese Einstellung um zusätzlich zum Primärschlüssel weitere Schlüssel
 * zu definieren.
 */
				<UNIQUE>true|false</UNIQUE>

/* Das Feld "INDEX" kann entweder den Wert true, oder false annehmen, wobei false der
 * Defaultwert ist. Ist INDEX=true so wird eine sortierte Liste der Werte dieser Spalte
 * gespeichert, was das Sortieren und Suchen nach den in der Spalte gespeicherten Werten
 * beschleunigen kann.
 */
				<INDEX>true|false</INDEX>

/* Die Anweisungen für die GUI und das SDK können im Feld "DISPLAY" angegeben werden.
 * Dazu existieren jeweils zwei Einstellungen: "HIDDEN" und "READONLY".
 * Wobei "READONLY" bedeutet, dass diese Spalte nicht zum Editieren angezeigt werden soll.
 * Wie der Name schon sagt, bedeutet "HIDDEN", dass die Spalte in der Ausgabe gar nicht
 * auftauchen soll.
 * Es gibt jeweils eine Einstellung, für die Abfragen: "NEW", "EDIT", "VIEW" und "SEARCH".
 * Die Eigenschaften können jeweils global oder für jede Aktion einzeln gesetzt werden.
 */

/* Zunächst die Variante mit globalen Einstellungen
 */
				<DISPLAY>
					<HIDDEN>true|false</HIDDEN>
					<READONLY>true|false</READONLY>
				</DISPLAY>				

/* Nun die Variante mit lokalen Einstellungen für jede Option
 */
				<DISPLAY>
					<HIDDEN>
						<NEW>true|false</NEW>
						<EDIT>true|false</EDIT>
						<SELECT>true|false</SELECT>
						<SEARCH>true|false</SEARCH>
					</HIDDEN>
					<READONLY>
						<NEW>true|false</NEW>
						<EDIT>true|false</EDIT>
					</READONLY>
				</DISPLAY>
			</Name der Spalte>

/*
 * Für die Datentypen integer, ip und time kann für das Feld "REQUIRED" der Wert "AUTO"
 * gesetzt werden. Dies bedeutet, dass der Wert automatisch erzeugt wird.
 * Für time    = das aktuelle Datum als Unix-Timestamp
 * Für ip      = die IP des Besuchers
 * Für integer = autoincrement beziehungsweise der Wert einer Sequence
 */
			<Name der Spalte>
				<TYPE>integer|ip|time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
			</Name der Spalte>

/*
 * Für den Datentyp select kann eine Aufzählung der erlaubten Werte angegeben werden.
 *
 * Die Semantik kann man sich relativ leicht merken:
 * + Die GUI stellt Spalten vom Typ select in Formularen als Select-Feld dar.
 * + Die Darstellung im Schema erinnert ebenfalls an ein Select-Formularfeld in HTML.
 */
			<Name der Spalte>
				<TYPE>select</TYPE>
				<DEFAULT>
					<defaultwert>Beschriftung</defaultwert>
					<option 1>Beschriftung 1</option 1>
					<option 2>Beschriftung 2</option 2>
				</DEFAULT>
			</Name der Spalte>

/*
 * Für den Datentyp image können die maximale Dateigröße, Breite und Höhe der Grafik
 * angegeben werden. Ist das Bild zu klein oder zu groß, wird es automatisch
 * auf die angegebene Größe gebracht. Mit dem Wert "Ratio" wird angegeben, ob dabei
 * das Verhältnis von Breite zu Höhe erhalten bleiben soll (true) oder nicht (false).
 * Falls "Ratio" auf "true" gesetzt wird, entsteht eventuell ein Rand um das Bild herum.
 * Deshalb kann eine Hintergrundfarbe angegeben werden.
 * Diese Hintergrundfarbe füllt den unbenutzten Bereich um die Grafik herum aus.
 *
 * Der Formulargenerator kann (wenn er verwendet wird) automatisch ein Upload-Feld
 * erzeugen, mit dem Grafiken an den Server übertragen werden können.
 * Bei einem Upload wird automatisch ein Thumbnail in der Größe 75x75px erzeugt.
 * Der Formulargenerator zeigt beim Aufrufen des Datensatzes automatisch das Thumbnail
 * und einen Link zur vollständigen Grafik an.
 *
 * Grafiken werden aus Gründen der Performance NICHT als Blob in der Datenbank
 * gespeichert, sondern in einem nicht-öffentlichen Verzeichnis im Dateisystem.
 * In der Datenbank wird lediglich der Dateipfad der Grafik hinterlegt.
 * Beim Löschen des Datensatzes (über die Datenbank-API das Frameworks) werden die
 * mit dem Datensatz assoziierte Grafik und das Thumbnail ebenfalls gelöscht.
 */
			<Name der Spalte>
				<TYPE>image</TYPE>
				<LENGTH>max. Upload-Größe in Byte</LENGTH>
				<WIDTH>Breite in Pixel</WIDTH>
				<HEIGHT>Höhe in Pixel</HEIGHT>
				<RATIO>true|false</RATIO>
				<BACKGROUND>
					<0>Zahl 0-255 (rot)</0>
					<1>Zahl 0-255 (grün)</1>
					<2>Zahl 0-255 (blau)</2>
				</BACKGROUND>
			</Name der Spalte>


		</CONTENT>
	</Name der Tabelle>

/* Hier können weitere Tabellen folgen.
 */

</TABLES>

Betrachten Sie zunächst ein ganz einfaches Beispiel:

/* Der Kopf ist normalerweise immer gleich: */

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>

/* Danach folgt die Definition der Tabellen der Datenbank */

<TABLES>
	/* hier die Tabelle 'foo' */
	<foo>
		/* Jede Tabelle muss 1 Primärschlüssel haben */
		<PRIMARY_KEY>foo_id</PRIMARY_KEY>
		/* nun folgen die Spalten */
		<CONTENT>
			/* zuerst der Primärschlüssel */
			<foo_id>
				<TYPE>integer</TYPE>
				<LENGTH>8</LENGTH>
			/* REQUIRED=auto erzeugt eine "autoincrement" Spalte */
				<REQUIRED>auto</REQUIRED>
			/* HIDDEN=true macht das Feld für Besucher unsichtbar */
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</foo_id>
			/* jetzt die restlichen Felder */
			<foo_title>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Titel</DESCRIPTION>
			</foo_title>
			<foo_text>
				<TYPE>text</TYPE>
				<REQUIRED>false</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
			</foo_text>
		</CONTENT>
	</foo>
	/* eine zweite Tabelle */
	<bar>
		<PRIMARY_KEY>bar_id</PRIMARY_KEY>
		<CONTENT>
			<bar_id>
				<TYPE>string</TYPE>
				<LENGTH>32</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</bar_id>
			<bar_value>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Wert</DESCRIPTION>
			</bar_value>
			<bar_time>
				<TYPE>time</TYPE>
				<REQUIRED>auto</REQUIRED>
				<DESCRIPTION>Zeit</DESCRIPTION>
			</bar_time>
		</CONTENT>
	</bar>
</TABLES>

Im Folgenden ein Beispiel für eine etwas komplexere Datenbank. Dargestellt ist die Datenstruktur der Gästebuch-Anwendung:

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>
<TABLES>
	<guestbook>
		<PRIMARY_KEY>guestbook_id</PRIMARY_KEY>
		<CONTENT>
			<guestbook_id>
				<TYPE>integer</TYPE>
				<LENGTH>5</LENGTH>
				<DESCRIPTION>Id (PK)</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>				
			</guestbook_id>
			<profile_id>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Id (FK)</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>				
			</profile_id>
			<guestbook_ip>
				<TYPE>ip</TYPE>
				<LENGTH>15</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>				
			</guestbook_ip>
			<guestbook_name>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Name</DESCRIPTION>
			</guestbook_name>
			<guestbook_message>
				<TYPE>text</TYPE>
				<LENGTH>3000</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
			</guestbook_message>
			<guestbook_mail>
				<TYPE>mail</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Mail</DESCRIPTION>
			</guestbook_mail>
			<guestbook_homepage>
				<TYPE>string</TYPE>
				<LENGTH>512</LENGTH>
				<DESCRIPTION>Homepage</DESCRIPTION>
			</guestbook_homepage>
			<guestbook_messenger>
				<DESCRIPTION>Messenger</DESCRIPTION>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
			</guestbook_messenger>
			<guestbook_msgtyp>
				<DESCRIPTION>Typ</DESCRIPTION>
				<TYPE>select</TYPE>
				<LENGTH>5</LENGTH>
				<DEFAULT>
					<icq>ICQ</icq>
					<aol>AOL</aol>
					<yahoo>Yahoo!</yahoo>
					<msn>MSN</msn>
				</DEFAULT>
			</guestbook_msgtyp>
			<guestbook_opinion>
				<TYPE>select</TYPE>
				<LENGTH>1</LENGTH>
				<CONSTRAINT>
					<INSERT>$VALUE >= 0 && $VALUE <= 5</INSERT>
					<UPDATE>$VALUE >= 0 && $VALUE <= 5</UPDATE>
				</CONSTRAINT>
				<DEFAULT>
					<0>unentschlossen</0>
					<1>sehr gut</1>
					<2>gut</2>
					<3>befriedigend</3>
					<4>ausreichend</4>
					<5>ungenügend</5>
				</DEFAULT>
				<DESCRIPTION>Meinung</DESCRIPTION>
			</guestbook_opinion>
			<guestbook_date>
				<TYPE>time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Datum/Zeit</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>
						<NEW>true</NEW>
                    			</HIDDEN>
				</DISPLAY>
			</guestbook_date>
			<guestbook_comment>
				<TYPE>text</TYPE>
				<LENGTH>1024</LENGTH>
				<REQUIRED>false</REQUIRED>
				<DESCRIPTION>Kommentar</DESCRIPTION>
				<DISPLAY>
					<READONLY>
						<EDIT>true</EDIT>
					</READONLY>
				</DISPLAY>
			</guestbook_comment>
			<guestbook_is_registered>
				<TYPE>integer</TYPE>
				<LENGTH>1</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DEFAULT>0</DEFAULT>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>				
			</guestbook_is_registered>
		</CONTENT>
	</guestbook>
</TABLES>

Zusammenhang zwischen Datenbankschemata und der Templatefunktion [%create ...%]

Die Funktion "create" (siehe Abschnitt zu Templates) dient dem Aufruf des Formulargenerators. Der Formulargenerator ist in der Lage, mit den Informationen aus dem Datenbankschema selbstständig Formulare zum Anzeigen, Durchsuchen, Erstellen, Editieren und Löschen von Datensätzen zu generieren. Notwendige Abfragen der Datenbank führt er automatisch aus. In Datenbankschemata wird der Tag "DISPLAY" verwendet, um das Verhalten des Formulargenerators zu steuern. Dieser Tag erlaubt das Ein- oder Ausblenden einzelner Spalten einer Tabelle, abhängig vom Typ des gerade angezeigten Formulars.

Darstellung der Datentypen

Der Formulargenerator zeigt Tabellenspalten abhängig von Ihrem Typ unterschiedlich an. Im Folgenden eine Auflistung der Typen mit Screenshots für die jeweilige Darstellung in der GUI.

Typ der Spalte Darstellung in GUI Beschreibung
integer, float, string Inputfeld Einzeilige Texte und Zahlen werden beim Editieren der Spalte als Inputfeld dargestellt. Ist die Spalte nicht editierbar, wird der Inhalt als Textzeile dargestellt.
boolean Checkbox Eingaben vom Typ "boolean" werden beim Editieren als Checkbox repräsentiert. Beim Anzeigen der Spalte, wird eine Grafik eingefügt, welche den Status des Feldes repräsentiert.
text Textarea Mehrzeilige Texte werden beim Editiren als Textareafelder dargestellt. Ist die Spalte nicht editierbar, wird der Inhalt als Fließtext dargestellt. Bei zu langen Texten werden Scrollbalken erzeugt (CSS: "overflow: auto").
url, ip Inputfeld Der Datentyp "ip" bietet die Möglichkeit, automatisch die IP-Adresse des Besuchers zu speichern. IP-Adressen werden auf Ihre syntaktische Gültigkeit geprüft. In der Regel werden Spalten dieses Typs nicht als editierbar markiert. Falls doch, wird ein Inputfeld angezeigt. Der Datentyp "url" entspricht dem Typ "string", mit dem Unterschied, dass geprüft wird ob die Eingabe eine syntaktisch korrekte URL ist.
mail Inputfeld Beim Editieren wird für diesen Datentyp wird ein Inputfeld angezeigt. Die Eingaben werden syntaktisch auf Gültigkeit geprüft.
mail (Quelltext) HTML-Quelltext Eingaben vom Datentyp "mail" werden bei der Darstellung im Browser automatisch verschlüsselt, um Datendiebstahl zu erschweren. Dies gilt grundsätzlich für alle angezeigten Mailadressen. Die Umsetzung erfolgt automatisch in der Darstellungsschicht des Frameworks, Eingriffe von Hand sich daher unnötig.
select Selectbox Der Datentyp "select" ist ein "Aufzählungstyp" (Enumeration). Beim Editieren wird eine Select-Box erzeugt. Der Inhalt der Box kann im Datenbankschema vorgegeben werden. Falls das Feld ein Fremdschlüssel ist (d.h. falls auf dieser Spalte ein Foreign-Key Constraint existiert), wird das Menü automatisch mit den Einträgen der verlinkten Tabelle augefüllt. Dabei kann angegeben werden, aus welchen Spalten die Beschriftungen und Werte entnommen werden sollen.
time (Editieren) 3 Selectboxen Beim Editieren werden für Spalten vom Typ "time" Selectboxen angezeigt, welche die Eingabe erleichtern.
time (Anzeigen) Zeitangabe: Deutsch, Englisch, Russisch Eingaben vom Typ "time" werden automatisch als Timestamp gespeichert und in der GUI als Datum dargestellt. Die Art der Darstellung kann im Administrationsmenü ausgewählt werden und passt sich den gewählten Spracheinstellungen automatisch an. Das Framework stellt das Datum stets automatisch synchron zur Zeitzone des Besuchers und in seiner jeweiligen Landessprache dar.
array (Editieren) Liste Eingaben vom Typ "array" können mehrdimensionale Arrays sein. Diese werden beim Editieren als Paare von Schlüsseln und Werten dargestellt. Durch Klick auf "entfernen" wird ein Wert gelöscht, ein Klick auf "neuen Eintrag speichern" fügt einen neuen Wert hinzu.
array (Anzeigen) Baummenü Beim Anzeigen werden die Werte als mehrdimensionale, aufklappbare Baummenüs dargestellt. Beim Überfahren eines Schlüssels mit der Maus, öffnet sich eine Liste der Einträge, welche diesem Schlüssel zugeordnet sind.
image Uploadfeld Spalten vom Datentyp "image" werden als Thumbnail mit einem Uploadfeld zum Einfügen oder Ersetzen der gespeicherten Grafik dargestellt. Beim Klick auf das Thumbnail wird die vollständige Grafik in einem neuen Fenster geöffnet. Die Grafikdatei wird beim Upload automatisch geprüft und konvertiert. Für die Konvertierung können im Datenbankschema zusätzliche Optionen, wie zum Beispiel die gewünschte Größe, angegeben werden.
file Uploadfeld Der Datentyp "file" dient dem Speichern von Dateien ("binary large objects"). Beim Editieren wird ein Uploadfeld zum Hochladen einer neuen Datei und ein Button für den Download der aktuell gespeicherten Datei angezeigt. Die Dateien selbst verbleiben nach dem Upload aus Gründen der besseren Performance im Dateisystem. Um Speicherplatz zu sparen, werden Sie automatich GZip-komprimiert. Die Komprimierung sorgt außerdem dafür, dass die auf dem Server gespeicherten Dateien nicht ausführbar sind und ein potentieller Angreifer den Upload nicht missbrauchen kann, um Schadcode zu übertragen. Beim Download der Datei wird diese automatisch entpackt, so dass der Nutzer keine Nachteile aus der Komprimierung erfährt und auch kein Dekomprimierungsprogramm installiert haben muss. Um trotzdem einen schnelleren Download zu gewährleisten, werden die Daten, falls der Browser des Nutzers diese Funktionalität anbietet, automatisch als komprimierter Datenstrom übertragen. Der Browser übernimmt das Entpacken selbstständig. Ein Eingreifen von Hand ist nicht erforderlich. Die Steuerung dieses Features übernimmt das Framework selbstständig und vollautomatisch.

Gegenüberstellung von Datentypen und deren Darstellung durch den Formuargenerator

besondere Konfiguration der Datentypen "image" und "file"

Das Downloaden von Dateien oder Öffnen von Grafiken bei Spalten vom Datentyp "image" oder "file", wird über die Aktion "download_file" realisiert, welche Sie im Plugin "default_library" finden. Dies geschieht normalerweise automatisch.

Aus Sicherheitsgründen ist der Zugriff auf diese Aktion per Voreinstellung auf Nutzer der Nutzergruppe "Admin" beschränkt. Dies ist die Nutzergruppe mit dem höchsten Sicherheitslevel. Wenn Sie auch Nutzern mit einem geringeren Sicherheitslevel Zugriff auf diese Daten gestatten wollen, müssen Sie die Sicherheitsbeschränkung dieser Aktion herabsetzen.

Um dies zu tun, editieren Sie bitte die Datei "plugins/default_library.config" in einem Texteditor Ihrer Wahl. Suchen Sie in dieser Datei den folgenden Abschnitt.

<DOWNLOAD_FILE>
    <TYPE>primary</TYPE>
    <MODE>1</MODE>
    <TEMPLATE>NULL</TEMPLATE>
    <PERMISSION>100</PERMISSION>
</DOWNLOAD_FILE>

Der Sicherheitslevel wird dargestellt als Zahl zwischen 0 und 100, wobei 100 der Gruppe "Admin" und 0 der Gruppe "Guest" entspricht. Ändern Sie den Wert "100" auf eine Zahl, die Ihnen angemessen erscheint.

Einige Vorschläge finden Sie in der folgenden Tabelle:

Nutzergruppe Sicherheitslevel
Gast (guest) 0
registrierter Nutzer (registered) 1
Moderator (mod) 30
Besitzer (owner) 75
Administrator (admin) 100

Gegenüberstellung von Nutzergruppe und Sicherheitslevel

In diesem Beispiel wurde der Wert auf "1" geändert.

<DOWNLOAD_FILE>
    <TYPE>primary</TYPE>
    <MODE>1</MODE>
    <TEMPLATE>NULL</TEMPLATE>
    <PERMISSION>1</PERMISSION>
</DOWNLOAD_FILE>

Speichern Sie anschließend die Datei.

Damit die Änderungen wirksam werden, müssen Sie im Administrationsmenü des Yana Frameworks den Plugin-Cache aktualisieren.


Abbildung: Schaltfläche zum Aktualisieren des Plugin-Cache

Nach Klick auf die Schaltfläche werden die geänderten Einstellungen sofort wirksam.

yana framework by:Homepage: Thomas Meyer, www.yanaframework.net