Redaxo-SQL-Klasse
Diese Klasse erleichtert etwas den Umgang mit Datenbanken in Redaxo. Es stehen Hilfsmethoden wie fetch(), iquery() und count() zur Verfügung. Außerdem kann das WV_SQL-Objekt als Iterator verwendet werden.
<?php
/**
* @defgroup internal Interne API
*/
/**
* Hilfsklasse für die SQL-Verwaltung
*
* Diese Klasse stellt einige komfortable Methoden zum Umgang mit der Datenbank
* bereit. Ihre Verwendung ist der Redaxo-Standardklasse in jedem Fall
* vozuziehen.
*
* Eine Instanz dieser Klasse kann direkt als Iterator (z.B. in einer
* foreach-Schleife) verwendet werden.
*
* @ingroup internal
*/
class WV_SQL implements Iterator {
const ASSOC = 0; ///< int gibt an, dass nur ein assoziatives Array geholt werden soll
const BOTH = 1; ///< int gibt an, dass ein sowohl assoziatives als auch numerisches Array geholt werden soll
const NUM = 2; ///< int gibt an, dass nur ein numerisches Array geholt werden soll
private $sql = null; ///< WV_SQL das verwendete %WV_SQL-Objekt
private $lastQuery = ''; ///< string die zuletzt ausgefühte Abfrage
private $numRows = array(0); ///< int Anzahl der Zeilen in den einzelnen Results
private $numCols = array(0); ///< int Anzahl der Spalten in den einzelnen Results
private $result = array(null); ///< array Array aus Result-Objekten
private $currentRow = array(null); ///< array für den Iterator
private $iteratorMode = array(self::ASSOC); ///< array die Iterator-Modi für jedes Result
private $depth = 0; ///< int die aktuelle Tiefe (Anzahl an Results)
private static $instance = null; ///< rex_sql das rex_sql-Objekt, das intern verwendet wird
/**
* Singleton
*
* Diese Methode gibt die einzig erlaubte WV_SQL-Instanz zurück.
*
* @return WV_SQL die Instanz der Klasse
*/
public static function getInstance() {
if ( !self::$instance ) self::$instance = new WV_SQL(rex_sql::getInstance());
return self::$instance;
}
/**
* Konstruktur
*
* @param rex_sql $sql das zu verwendende rex_sql-Objekt
*/
private function __construct(rex_sql $sql) {
$this->sql = $sql;
}
/**
* Tabellen-Präfix ermitteln
*
* Wrapper um $REX, um zu vermeiden, dass die Methoden des AddOns immer
* wieder "global REX" schreiben müssen.
*
* @return string das Tabellen-Präfix von Redaxo
*/
public static function getPrefix() {
global $REX;
return $REX['TABLE_PREFIX'];
}
/**
* Datenbank auswählen
*
* Ändert die ab sofort zu verwendende Datenbank.
*
* @param int $databaseID die ID der Datenbank (1 oder 2)
*/
public function selectDB($databaseID) {
$this->sql->selectDB($databaseID);
}
/**
* Hilfsmethode für genau eine Zeile
*
* Diese Methode dient dazu, genau eine Zeile zu holen. Sie gibt im
* Erfolgsfall kein true, sondern die geholte Zeile zurück, wodurch ein
* Aufruf von row() entfällt. Sollte das Ergebnis nur eine Spalte haben, so
* wird direkt dieser Wert zurückgeliefert (und kein Array mit einem
* Element).
*
* @param string $what Spalten, die geholt werden sollen
* @param string $from Tabelle, aus der gelesen werden soll
* @param string $where WHERE-Kriterium
* @param int $mode der Modus, in dem die Zeilen dann zurückgegeben werden sollen
* @param bool $includeRexPrefix wenn true, wird $REX['TABLE_PREFIX'] vor den Tabellennamen gesetzt
* @return mixed false im Falle eines Fehlers, sonst mixed oder ein Array (je nach Spaltenanzahl)
*/
public function fetch($what, $from, $where = '1', $mode = self::ASSOC, $includeRexPrefix = true) {
$query = sprintf('SELECT %s FROM %s%s WHERE %s LIMIT 1',
$what,
$includeRexPrefix ? self::getPrefix() : '',
$from,
$where);
if ( ($result = $this->query($query, '', $mode, true)) === false ) {
return false;
}
$row = array();
switch ( $mode ) {
case self::BOTH : $row = mysql_fetch_array($result); break;
case self::NUM : $row = mysql_fetch_row($result); break;
case self::ASSOC:
default : $row = mysql_fetch_assoc($result);
}
mysql_free_result($result);
if ( $row === false ) {
return false;
}
if ( count($row) == 1 ) {
$ret = array_values($row);
return $ret[0];
} else {
return $row;
}
}
/**
* Abfrage für den Iterator ausführen
*
* Diese Methode arbeitet exakt so wie auch query(), mit dem Unterschied,
* dass sie im Erfolgsfall nicht true, sondern eine Referenz auf das Objekt
* zurückgibt, über das direkt iteriert werden kann. So kann man bei kurzen
* Abfragen nochmal eine Zeile Code einsparen.
*
* @param string $query die auszuführende Abfrage
* @param string $replaceRexPrefix wenn nichtleer, wird in dem Query jedes Vorkommen durch TABLE_PREFIX ersetzt
* @param int $mode der Modus, in dem die Zeilen dann zurückgegeben werden sollen
* @return mixed ein leeres Array, wenn etwas schief lief, oder eine Referenz auf sich selbst im Erfolgsfall
*/
public function iquery($query, $replaceRexPrefix = '', $mode = self::ASSOC) {
if ( !empty($replaceRexPrefix) ) {
$query = str_replace($replaceRexPrefix, self::getPrefix(), $query);
}
$this->lastQuery = $query;
if ( gettype($this->result[$this->depth]) == 'resource' ) {
mysql_free_result($this->result[$this->depth]);
}
$this->result[$this->depth] = mysql_query($query, $this->sql->identifier);
$this->currentRow[$this->depth] = null;
$this->iteratorMode[$this->depth] = $mode;
if ( $this->result[$this->depth] === false ) {
$this->numRows[$this->depth] = 0;
$this->numCols[$this->depth] = 0;
return array();
}
if ( is_resource($this->result[$this->depth]) ) {
$this->numRows[$this->depth] = mysql_num_rows($this->result[$this->depth]);
$this->numCols[$this->depth] = mysql_num_fields($this->result[$this->depth]);
} else {
$this->numRows[$this->depth] = 0;
$this->numCols[$this->depth] = 0;
}
return $this;
}
/**
* Abfrage ausführen
*
* Diese Methode führt genau eine Abfrage aus und liefert im Erfolgsfall
* true, sonst false zurück. Wird der Parameter $temp auf true gesetzt, so
* belegt die Abfrage nicht das aktuelle, interne Resultset, sondern legt
* eine neue Variable an und gibt auch diese nach erfolgter Abfrage zurück.
* fetch() nutzt diesen Mechanismus, um vom internen Resultset unabhängig zu
* sein.
*
* @param string $query die auszuführende Abfrage
* @param string $replaceRexPrefix wenn nichtleer, wird in dem Query jedes Vorkommen durch TABLE_PREFIX ersetzt
* @param int $mode der Modus, in dem die Zeilen dann zurückgegeben werden sollen
* @param bool $temp true, wenn die Abfrage nicht das interne Resultset belegen soll
* @return mixed false bei einem Fehler, true wenn alles i.O.
*/
public function query($query, $replaceRexPrefix = '', $mode = self::ASSOC, $temp = false) {
if ( !empty($replaceRexPrefix) ) {
$query = str_replace($replaceRexPrefix, self::getPrefix(), $query);
}
$this->lastQuery = $query;
if ( $temp ) {
$result = mysql_query($query, $this->sql->identifier);
return $result ? $result : false;
}
if ( gettype($this->result[$this->depth]) == 'resource' ) {
mysql_free_result($this->result[$this->depth]);
}
$this->result[$this->depth] = mysql_query($query, $this->sql->identifier);
$this->currentRow[$this->depth] = null;
$this->iteratorMode[$this->depth] = $mode;
if ( $this->result[$this->depth] === false ) {
$this->numRows[$this->depth] = 0;
$this->numCols[$this->depth] = 0;
return false;
}
if ( is_resource($this->result[$this->depth]) ) {
$this->numRows[$this->depth] = mysql_num_rows($this->result[$this->depth]);
$this->numCols[$this->depth] = mysql_num_fields($this->result[$this->depth]);
} else {
$this->numRows[$this->depth] = 0;
$this->numCols[$this->depth] = 0;
}
return true;
}
/**
* Liefert ein Array zurück
*
* Diese Methode läuft über ein Resultset und gibt ein
* normales Array mit dem Wert zurück. Es darf dazu nur
* ein einzelnes Feld selektiert werden. Werden zwei
* Felder selektiert, so ist das erste der Schlüssel und
* der zweite der Wert des Ergebnisarrays.
*
* @param string $query die auszuführende Abfrage
* @param string $replaceRexPrefix wenn nichtleer, wird in dem Query jedes Vorkommen durch TABLE_PREFIX ersetzt
* @return array ein Array mit den Werten
*/
public function getArray($query, $replaceRexPrefix = '') {
if ( !$this->query($query, $replaceRexPrefix, self::ASSOC) ) return array();
if ( $this->cols() == 0 ) return array();
$result = array();
foreach ( $this as $row ) {
if ( $this->cols() == 1 ) $result[] = reset($row);
elseif ( $this->cols() == 2 ) $result[reset($row)] = next($row);
else {
$columns = array_slice(array_keys($row),1);
foreach ( $columns as $col ) {
$result[reset($row)][$col] = $row[$col];
}
}
}
return $result;
}
/**
* Zeilen zählen
*
* Diese Methode liefert die Anzahl der Zeilen in einer Tabelle, die auf ein
* bestimmtes Kriterium passen.
*
* Intern wird fetch("COUNT(*) AS count", $table, $where) aufgerufen.
*
* @param string $table die zu untersuchende Tabelle
* @param string $where WHERE-Kriterium
* @param bool $includeRexPrefix wenn true, wird $REX['TABLE_PREFIX'] vor den Tabellennamen gesetzt
* @return mixed false im Falle eines Fehlers, sonst int
*/
public function count($table, $where = '1', $includeRexPrefix = true) {
$ret = $this->fetch('COUNT(*) AS count', $table, $where, $includeRexPrefix);
return $ret ? intval($ret['count']) : false;
}
/**
* Liefert eine Zeile
*
* Diese Methode liefert genaue eine Zeile aus dem Resultset, wird aber
* durch die Iterator-Fähigkeit eher selten benötigt.
*
* @param string $mode der Modus, in dem die Zeilen dann zurückgegeben werden sollen
* @return mixed ein lerres Array im Falle eines Fehlers, sonst ein gefülltes Array
*/
public function row($mode = self::ASSOC) {
if ( $this->result[$this->depth] === null ) {
return array();
}
switch ( $mode ) {
case self::BOTH : $row = mysql_fetch_array($this->result[$this->depth]); break;
case self::NUM : $row = mysql_fetch_row($this->result[$this->depth]); break;
case self::ASSOC:
default : $row = mysql_fetch_assoc($this->result[$this->depth]);
}
return $row;
}
/**
* die letzte ID
*
* Gibt die zuletzt durch AUTO_INCREMENT eingefügte ID zurück.
*
* @return int die letzte ID
*/
public function lastID() {
return mysql_insert_id($this->sql->identifier);
}
/**
* die betroffenen Zeilen
*
* Gibt die Anzahl der von der letzten Abfrage betroffenen Zeilen zurück.
*
* @return int die betroffenen Zeilen
*/
public function affectedRows() {
return mysql_affected_rows($this->sql->identifier);
}
/**
* Neues Resultset anlegen
*
* Um verschachtelte Abfragen zu ermöglichen, kann mit dieser Methode ein
* neues, leeres Resultset auf den Stack gelegt werden, das danach für alle
* weiteren Abfragen herhalten wird.
*/
public function in() {
++$this->depth;
$this->result[$this->depth] = null;
$this->currentRow[$this->depth] = null;
$this->iteratorMode[$this->depth] = self::ASSOC;
$this->numRows[$this->depth] = 0;
$this->numCols[$this->depth] = 0;
}
/**
* Vorheriges Resultset nutzen
*
* Entfernt das oberste Resultset und setzt den Zeiger auf das vorherige.
* Entspricht einer POP-Operation bei Stacks. Jeder in()-Aufruf sollte
* irgendwann von einem out()-Aufruf gefolgt werden.
*/
public function out() {
if ( is_resource($this->result[$this->depth]) ) {
mysql_free_result($this->result[$this->depth]);
}
--$this->depth;
if ( $this->depth < 0 ) {
$this->depth = 0;
}
}
/**
* die letzte Abfrage
*
* Gibt die zuletzt ausgeführte Afrage zurück
*
* @return string die letzte Abfrage
*/
public function getLastQuery() {
return $this->lastQuery;
}
/**
* Anzahl der Zeilen
*
* Diese Methode gibt die Anzahl der mit der letzten Abfrage geholten Zeilen
* zurück. Dieser Wert ist nicht immer definiert (z.B. nicht für
* UPDATE-Queries).
*
* @return int die Anzahl der Zeilen
*/
public function rows() {
return $this->numRows[$this->depth];
}
/**
* Anzahl der Spalten
*
* Diese Methode gibt die Anzahl der mit der letzten Abfrage geholten
* Spalten zurück. Dieser Wert ist nicht immer definiert (z.B. nicht für
* UPDATE-Queries).
*
* @return int die Anzahl der Spalten
*/
public function cols() {
return $this->numCols[$this->depth];
}
/**
* Gibt den letzten Fehlercode zurück.
*
* @return int den letzten Fehlercode
*/
public function getErrno() {
return mysql_errno($this->sql->identifier);
}
/**
* Gibt die letzte Fehlermeldung zurück.
*
* @return string die letzte Fehlermeldung
*/
public function getError() {
return mysql_error($this->sql->identifier);
}
// =========================================================================
// ITERATOR-METHODEN
// =========================================================================
///@cond INCLUDE_ITERATOR_METHODS
public function current() {
return $this->currentRow[$this->depth];
}
public function next() {
$this->currentRow[$this->depth] = $this->row($this->iteratorMode[$this->depth]);
}
public function key() {
return null;
}
public function valid() {
if ( $this->result[$this->depth] === false ) {
return false;
}
// Wurde noch gar keine Zeile geholt? Dann holen wir das hier nach.
if ( $this->currentRow[$this->depth] === null ) {
$this->currentRow[$this->depth] = $this->row($this->iteratorMode[$this->depth]);
}
return is_array($this->currentRow[$this->depth]);
}
public function rewind() {
if ( mysql_num_rows($this->result[$this->depth]) > 0 ) {
mysql_data_seek($this->result[$this->depth], 0);
$this->currentRow[$this->depth] = null;
}
}
///@endcond
}
?>
Snippetdetails
- hinzugefügt: 16.12.2008
- aktualisiert: 24.01.2009
- Snippet herunterladen
Kommentar verfassen
Fehler gefunden? Doofer Code? Ein kleines "Danke!"? Hinterlasse einfach einen Kommentar.
Dein Kommentar wird erst nach einer manuellen Prüfung angezeigt.