DBUtil

Guite edited this page Nov 12, 2014 · 3 revisions
Clone this wiki locally

== DBUtil == DBUtil ist Zikulas spezifische, sehr mächtige und flexible Art auf die Datenbank zuzugreifen: *Durch die Verwendung von DBUtil funktionieren Module automatisch auf verschiedenen Datenbank-Management-Systemen (MySQL, PostGres, Oracle, MSQL) *DBUtil hilft Code zu sparen, indem viele typische Datenbank-Zugriffe auf eine oder wenige Zeilen zusammengekürzt werden können - wenniger Code bedeutet mehr Übersichtlichtkeit, weniger Bugs und mehr Sicherheit. *DBUtil löst einige komplexe Zikula-spezifische Datenbank-Aktionen automatisch: Zum Beispiel können Listen von Objekten automatisch nach Zugriffsrechten gefiltert werden. *Außerdem bietet DBUtil /Module/Meta-Daten und /Module/Attribution.

=== Einführung === Die Benutzung von DBUtil ist sehr einfach zugänglich, weil Du normalerweise kein SQL benutzen musst. Wo Du normalerweise alle möglichen Informationen zusammensuchen (DB-Zugang, Tabellenname, Felder usw.) und dann eine SQL-Abfrage zusammenbauen musst, kannst Du per DBUtil direkt auf die Objekte zugreifen. Das mag erst einmal fremd klingen, aber es spart wirklich jede Menge Code.

DBUtil verarbeitet Obejkte in Form von assoziativen Arrays, bei denen die Keys den Tabellenfeldnamen entsprechen - so wie Du sie in der /Module/pntables definiert hast:

 $id,
                    'name'           => $wert1,
                    'street'         => $wert2,
                    'zip'            => $wert3,
                    'city'           => $wert4);

So ein Objekt können wir auch einfach an die Präsentationsschicht per /Module/Aufbau#Pr%C3%A4sentationsschicht übergeben. Dann steht dieses Objekt als Ganzes im Template zur Verfügung. Statt dann auf einzelne Variablen (name, street, zip, ..) kannst Du dann auf das Objekt zugreifen:



 

Das hat für die folgende Bearbeitung eine Reihe Vorteile:

  • Es wird nur das Objekt an das Template geschickt (weniger Code)
  • Das Objekt wird auch als solches von einem Formular zurückgeschickt
  • Du musst nur das Objekt statt vieler Variablen bereinigen
  • Du kannst das Objekt direkt per DBUtil weiterverarbeiten (Updates, Inserts)

Normale ADOdb-Abfragen zum Beispiel für eine Liste von Artikeln, benötigt eine Menge Code, um die Abfrage zu erstellen, alle Zeilen auszulesen und sie dann noch durch die Zugriffsrechte zu filtern - Das hat früher immer zu unheimlichen Problemen geführt, wenn zum Beispiel die ersten 10 Artikel für eine Seite geholt werden sollten und dann 5 auf Grund der Permissions herausgefiltert wurden. DBUtil übernimmt diese Aufgabe.

Außerdem bereitet DBUtil alle Daten vor dem Eintrag in die Datenbank vor, so dass Du Dich wirklich nur um das Nötigste kümmern musst.

==== Objekte und Rückgabewerte ====

Eine wichtige Sache solltest Du noch über DBUtil wissen:

*Die Objekte werden an DBUtil als Referenz übergeben. Der Rückgabewert der aufgerufenen Funktion ist nur in sofern wichtig, als dass er true, false oder eine ID sein kann. Es kann aber auch das Objekt selbst durch den Aufruf aktualisiert worden sein. Wozu das nützlich sein kann, erfährst Du später im Text.

=== SELECT === Selects sind mit DBUtil wirklich billig: Es gibt eine Methode, um nur ein Objekt und eine Methode, um eine Liste von Objekten zu holen. Darüber hinaus sind auch komplexere Abfragen mit JOINS möglich - dazu aber später mehr.

Zunächste die einfachste und die praktischste SELECT-Abfrage.

==== selectObjectByID ==== Diese Funktion holt anhand einer ID ein einzelnes Objekt aus der Datenbank. Sie gibt entweder die gesamten Daten inklusive /Module/Meta-Daten und /Module/Attribution als assoziatives Array zurück, oder false, wenn die ID nicht gefunden wurde. Die Funktion ersetzt also so etwas wie ''SELECT * FROM tablename WHERE id='$id';''

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectobjectbyid selectObjectByID]

'''Beispiel'''


In diesem Fall wurde angenommen, dass das Feld mit der ID "id" heißt. Wurde das Feld anders benannt, kann dieser Name in nächsten Parameter übergeben werden:


Wenn Du nicht das gesamte Datenobjekt, sondern nur einige Felder holen möchtest, kann Du diese Felder im nächsten Paremeter als Array angeben. Das wären dann im klassischen SQL Abfrage wie SELECT field1, field2 FROM tablename WHERE id='$id';


Du kannst selectObjectByID auch gleich die Permissions überprüfen lassen. Dazu aber später mehr, wenn Du mehrere Datenbank-Objekte holst. ==== Parameter ==== Hier erst einmal die Liste aller möglichen parametern für selectObjectByID:

{| border=1 class="wikitable" |'''tablename''' |Name der Tabelle laut [[/Module/pntables|pntables.php]] |- | '''id''' | ID des gesuchten Objektes |- | '''field''' | Name des ID-Feldes (optional) (default='id') |- | '''columnArray''' | Die gewünschten Felder - wenn nicht alle gewünscht sind (optional) (default=null) |- | '''permissionFilter''' | Zugriffsrechte, die überprüdft werden sollen (optional) (default=null) |}


==== selectObjectArray ==== Diese Funktion holt mehrere Objekte aus der Datenbank. Sie gibt entweder die gesamten Daten inklusive [[/Module/Meta-Daten|Meta-Daten]] und [[/Module/Attribution|Attribute]] als assoziatives Array zurück, oder false, wenn keine passenden Ergebnisse gefunden wurde. Die Funktion ersetzt also so etwas wie ''SELECT * FROM address'' Die Ergebnisse können dabei auch auf mehrere Seiten verteilt werden ("Pagination").

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectobjectarray selectObjectArray]

'''Beispiel:'''


==== WHERE ==== Wenn Du eine WHERE-Bedingung benötigst solltest Du 2 Dinge beachten: Die Tabellennamen kann DBUtil leicht aus den Definitionen in der [[/Module/pntables|pntables.php]] automatisch auslesen - die Feldnamen müssen aber noch "traditionell" ermittelt werden:


'''Hinweis:''' pnVarPrepForStore() ist nötig, bevor Du den String an die Abfrage übergibst.

==== ORDERBY ====

ORDERBY Bedingungen sind noch einfacher und DBUtil wird sie lesen und in korrektes SQL verwandeln:


oder


oder


Hier eine einfache WHERE- und ORDERBY-Einschränkung:


==== Pagination ==== Für die Verteilung von Ergebnislisten auf mehrere Seiten ("Pagination") benutzt Du einen Aufruf wie diesen, in dem 25 Adressen pro Seite geholt werden:


Wenn Du nun "startnum" mit jeder Seite erhöst, bekommst Du die gewünschte Seitenansicht.

'''Hinweis:''' Die Standardwerte für limitOffset und limitNumRows sind -1 wodurch die Pagination quasi ausgeschaltet ist.

==== Zugriffsrechte ==== Die Filterung von Ergebnissen nach Zugriffsrechten per DBUtil ist echt cool, weil es das alte Problem löst: Du holst die ersten 25 Artikel aus der Datenbank und laut Zugriffsrechte darf der aktuelle benutzer nur 20 davon sehen. DBUtil sorgt direkt dafür, dass nur die richtigen ersten 25 Artikel geholt werden.

Die nötigen Zugriffsrechte werden per Array an DBUtil übergeben. Das sieht dann so aus:

 'example',
                        'component_middle' => '',
                        'component_right'  => '',
                        'instance_left'    => 'address',
                        'instance_middle'  => '',
                        'instance_right'   => '',
                        'level'            => ACCESS_EDIT);

    $objArray = DBUtil::selectObjectArray('users', '', '', -1, -1, '', $permFilter);

In diesem Beispiel wird überprüft, ob der aktuelle Benutzer diese Rechte hat: {| border=1 class="wikitable" |Gruppe |example:: |address:: |ACCESS_EDIT |}

Entsprechend sind die Elemente dieses Arrays: {| border=1 class="wikitable" |Gruppe |component_left:component_middle:component_right |instance_left:instance_middle:instance_right |level |}

Du kannst auch ein Array dieses Arrays an DBUtil übergeben - dann werden verschiedene Rechte abgeprüft.

==== Parameter ==== {| border=1 class="wikitable" |'''tablename''' |Name der Tabelle laut [[/Module/pntables|pntables.php]] |obligatorisch | - |- | '''where''' | "WHERE" Bedingung | optional | default=' ' |- | '''orderby''' | "ORDERBY" Sortierung | optional | default='' |- | '''limitOffset''' | Ab Ergebnis Nummer # | optional | default=-1 |- | '''limitNumRows''' | Bis Ergebnis Nummer # | optional | default=-1 |- | '''assocKey''' | Name des Keys für das Assoziative Array | optional | default=' ' |- | '''permissionFilter''' | Filter für die Zugriffsrechte | optional | default=null |- | '''columnArray''' | Felder, die zurückgegeben werden sollen, wenn nicht alle gewünscht sind | optional | default=null |}


=== INSERT === Bevor Du aber Objekte aus der Datenbank holen kannst, musst Du zunächst welche hinein schreiben. Das funktioniert aber im Prinzip wie die Abfrage: Entweder Du fügst ein Objekt in die Datenbank oder ein Array von Objekten.

==== insertObject ==== Alle Datenbanktabellen sollten ein eindeutiges ID-Feld haben. DBUtil nimmt immer "id" als Namen dieses Feldes an, wenn kein anderer Feldname übergeben wird. DBUtil wird dann dem neuen Datenbankeintrag eine passende ID zuweisen. Die Feldnamen müssen mit denen in der [[/Module/pntables|pntables.php]] übereinstimmen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#insertobject insertObject]

'''Beispiel:'''

 'George',
                     'street' => 'Abbey Road',
                     'city'   => 'London');

    // 2. INSERT
    $result = DBUtil::insertObject($address, 'address');

$result enthält dann ein [[/ADOdb|ADOdb]] Recordset-Objekt oder false, wenn etwas schief gegangen ist.

Wie bereits in der Einführung geschrieben, wir bei einem erfolgreichen INSERT das ursprüngliche Objekt automatisch aktualisiert und mit der ID versehen, die ihm in der Datenbank zugewiesen wurde. Das Array sieht jetzt quasi so aus:

 '1', // Hier ist die neue ID!
          'name'   => 'George',
          'street' => 'Abbey Road',
          'city'   => 'London');

Wenn Du aus irgendeinem Grund dem Objekt keine automatische ID zuweisen lassen will, kannst Du das mit ''$force = true'' tun:

 '64',
                     'name'   => 'George',
                     'street' => 'Abbey Road',
                     'city'   => 'London');

    // force the insert
    $result = DBUtil::insertObject($address, 'address', true);

Klar, dass das nicht klappt, wenn die ID schon vergeben ist. Deswegen solltest Du wissen, was Du tust, wenn Du die IDs selbst vergibst.

Du kannst natürlich - wie bei den SELECTs - auch den Namen des ID-Feldes angeben:


==== insertObjectArray ==== Mit insertObjectArray kannst Du mehrere Objekte gleichzeitig in die Datenbank schieben. Dazu übergibst Du der Funktion ein Array aus Arrays. Die Feldnamen müssen natürlich auch hier mit denen in der [[/Module/pntables|pntables.php]] übereinstimmen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#insertobjectarray insertObjectArray]

'''Beispiel:'''

 'George',
                            'street' => 'Abbey Road',
                            'city'   => 'London'),
                      array('name'   => 'John',
                            'street' => 'Penny Lane',
                            'city'   => 'Liverpool'));
 
    // 2. INSERT
    $result = DBUtil::insertObjectArray($addresses, 'address');

=== UPDATE === Das Prinzip ist wieder das gleiche, wie bei SELECT und INSERT: Es gibt eine Funktion, um ein einzelnes Objekt zu aktualisieren und eines um mehrere Objekte zu aktualisieren. Auch hier müssen die Feldnamen mit denen in der [[/Module/pntables|pntables.php]] übereinstimmen - Aktualisiert werden nur die Felder, die im Array übergeben werden. Die anderen bleiben unberührt.

==== updateObject ==== updateObject führt ein UPDATE durch. Das assoziative Array, dass der Funktion als erster Parameter übergeben wird, sollte alle Felder samt Werte enthalten, die aktualisiert werden sollen. Die Feldnamen müssen natürlich auch hier mit denen in der [[/Module/pntables|pntables.php]] übereinstimmen. Für WHERE-Bedingungen, die sich auf das ID Feld beziehen, kannst Du den $where-Parameter weglassen und einfach die ID angeben - DBUtil baut daraus eine entsprechende Bedingung.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#updateobject updateObject]

'''Beispiel:'''

 4,
                     'name'         => 'Yusuff');
    DBUTil::updateObject ($address, 'address');

Diese Anweisung wird das Datenbank-Objekt mit der ID aktualisieren und den Eintrag für den Namen ändern.

Aber natürlich kannst Du auch selbst eine WHERE Bedingung bauen, wenn Du die ID nicht weißt:

 'Yusuff');

    // 3. Erstelle die WHERE-Bedingung
    $where    = "WHERE $address[name]='Cat'";

    // 4. Schick das UPDATE ab
    DBUTil::updateObject ($obj, 'customers', $where);

==== updateObjectArray ==== Analog zur insertObjectArray kannst Du hier mehrere Datensätze übergeben und ändern lassen. Im Gegensatz aber zu updateObject machen hier WHERE-Bedinungen keinen Sinn. Der Zugriff muss per ID erfolgen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#updateobjectarray updateObjectArray]

'''Beispiel:'''

 4,
                             'name'         => 'Yusuff'),
                       array('addressid'    => 1999,
                             'name'         => 'TAFKAP'));
    DBUtil::updateObjectArray ($addresses, 'address');

=== DELETE === Löschen funktioniert nun ein wenig anders. Hier gibt es nicht nur je eine Methode um ein oder mehrere Objekte zu bearbeiten, sondern drei Methoden:

==== deleteObjectByID ==== Mit dieser Funktion kannst Du Einträge anhand der ID löschen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#deleteobjectbyid deleteObjectByID]

'''Beispiel:'''


Wir können ein wenig mogeln und DBUtil eine falsche ID-Spalte vortäuschen. Statt ''addressid'' nehmen wir hier einfach mal ''city''


Das führt dazu, dass alle Einträge gelöscht werden, in denen "London" als ''city'' eingetragen ist. Eine nette Abkürzung.

==== deleteObject ==== Mit deleteObject kannst Du ein Objekt nehmen, um es in der Datenbank zu löschen - Du kannst Dir dann die WHERE-Bedingung sparen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#deleteobject deleteObject]

'''Beispiel:'''


==== deleteWhere ==== Mit dieser Funktion kannst Du mehrere DB-Einträge löschen. Wichtig ist dabei die WHERE-Bedingung: Wenn Du die leer lässt werden alle Einträge gelöscht.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#deletewhere deleteWhere]

'''Beispiel:'''


=== JOIN === Nicht immer reicht es, Daten nur aus einer Tabelle zu holen. In diesen Fällen müssen im klassischen SQL JOINS benutzt werden. Aber auch DBUtil bietet da etwas.

'''Weiterlesen:''' [http://www.sql-und-xml.de/sql-tutorial/tabellen-verknuepfen-mit-join.html JOIN - Normalisierte Tabellen für eine Abfrage wieder zusammenfassen] ==== $joinInfo ==== Der Schlüssel zum Umgang mit JOINs in DBUtil ist ein Array, der alle nötigen Informationen für den JOIN enthält. Die Datenbanknamen sind übrigens nicht nicht richtigen Namen, sondern deren Aliase:

{| border=1 class="wikitable" |'''join_table''' |Name der Tabelle, aus der Daten dazu geholt werden soll |- | '''join_field''' | Feld oder Feld-Array mit den Daten, die geholt werden sollen |- | '''join_method''' | Art des JOINS: "LEFT JOIN", "RIGHT JOIN", "INNER JOIN". Default: LEFT JOIN |- | '''object_field_name''' | Namen der Felder als Key im Ergebnis-Array. Diese sollten mit einem Prefix versehen vergeben werden, um Überschneidungen in den Keys mit der Haupttabelle zu vermeiden. |- | '''compare_field_table''' | Feld aus der Haupttabelle für den JOIN |- | '''compare_field_join''' | Feld aus der JOIN-Tabelle |}

Die JOIN-Funktionen funktionieren im Prinzip wie die normalen SELECTS, nur dass sie ein wenig anders heißen und jeweils noch den oben erklärten Parameter $joinInfo benötigen:

==== selectExpandedObject ==== selectExpandedObject sucht ein Objekt aus der Haupttabelle anhand der WHERE-Bedingung und holt dann die Daten aus dem JOIN dazu.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectexpandedobject selectExpandedObject]

==== selectExpandedObjectArray ==== selectExpandedObjectArray holt mehrere Objekte und fügt die Daten aus dem JOIN hinzu.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectexpandedobjectarray selectExpandedObject]

==== selectExpandedObjectByID ==== selectExpandedObjectByID holt ein Objekt anhand seiner ID und fügt die Daten aus dem JOIN hinzu.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectexpandedobjectbyid selectExpandedObjectByID]

==== selectExpandedObjectCount ==== selectExpandedObjectCount zählt die Zeilen, die durch den JOIN betroffen sind.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#selectobjectcount selectExpandedObjectCount]

==== Beispiel ==== Hier eine verkürzte [[/Module/pntables|pntables.php]] - Die soll nur zeigen, welche Felder es in der Datenbank gibt:

 'pn_addressid',
                                           'name'      => 'pn_name',
                                           'street'    => 'pn_street',
                                           'zip'       => 'pn_zip',
                                           'city'      => 'pn_city');

// Tabelle 2
$pntable['example_albums'] = DBUtil::getLimitedTablename('example_albums');
$pntable['example_albums_column'] = array('albumid'    => 'pn_albumid',
                                          'addressid'  => 'pn_addressid',
                                          'name'       => 'pn_name',
                                          'year'       => 'pn_year';

Bauen wir uns die JOIN-Info, um die Alben zusammen mit den Namen der Künstler zu holen:

 'example_address',
                     'join_field'          => 'name',
                     'join_method'         => 'LEFT JOIN',
// Der "name" aus der Adress-Tabelle soll im Ergebnis "artist_name" heißen, weil auch das Album ein "name" Feld hat:
                     'object_field_name'   => 'artist_name', 
                     'compare_field_table' => 'addressid',
                     'compare_field_join'  => 'addressid'):

Wenn Du übrigens auf Tabellen aus anderen Modulen zugreifen willst, musst Du pnModDBInfoLoad('MODULNAME') aufrufen, um auch dessen Tabellen-Defintionen zu holen.

Nun kannst Dir die die Daten holen:


Das Ergebnis enthält dann alle Felder aller Alben aus der Datenbank zusammen mit dem Namen des Künstlers, der über die addressid verknüpft ist.

=== TABLE === Die folgenden Funktionen sind normalerweise nur in der [[/Module/pninit|pninit.php]] zu benutzen. Schauen wir uns einige Beispiele daraus an:

==== createTable ==== Tabellen anlegen kannst Du mit createTable;

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#createtable createTable]

'''Beispiel:'''


==== dropTable ==== Tabellen löschen kannst Du mit dropTable.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#dropTable dropTable]

'''Beispiel:'''


==== changeTable ==== changeTable ist vermutlich eine der interessantesten Funktionen. Du kannst einfach die neu Struktur einer Tabelle angeben und DBUtil kümmert sich um alles Notwendige.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#changeTable changeTable]

'''Beispiel:''' Hier könnten wir zum Beispiel das title-Feld von 255 auf 50 Zeichen verkürzen und das debug-Feld in Form eines BLOB hinzufügen:


=== INDEX === Mit Indizes lassen sich Daten aus Tabellen schneller aufrufen. Du solltest also auf die Felder, über die häufig auf die Daten zugegriffen wird einen Index setzen. Auch die INDEX-Funktionen benötigst Du nur in der [[/Module/pninit|pninit.php]] ==== createIndex ==== Mit createIndex kannst Du Indizes auf einzelne oder mehrere Felder setzen:

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#createIndex createIndex]

'''Beispiel:'''


==== dropIndex ==== Mit dropIndex kannst Du die Indizes auch wieder entfernen.

'''Referenz:''' [http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html#dropIndex dropIndex]

'''Beispiel:'''


=== Daten === Wer mit Datums-Feldern arbeitet sollte wissen, dass der Inhalt von [[/ADOdb|ADOdb]] immer ein String in der Form von 'YYYY-DD-MM HH:MM:SS' ist. Damit kann aber wiederum [http://phpxref.zikula.de/includes/pnobjlib/DateUtil.class.php.html DateUtil] gut umgehen:

#php

=== Links === *[http://phpxref.zikula.de/includes/pnobjlib/DBUtil.class.php.html DBUtil Referenz]