Schnellnavigation:

Kategorien

« September 2010»
S M T W T F S
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Kopieren Sie diesen Link in Ihren RSS-Reader

RSS 0.91Nachrichten
RSS 2.0Nachrichten

In eigener Sache

Peter Linzenkirchner, Lisardo EDV Beratung in Augsburg. Freelance und Partner für Design- und Webagenturen in Augsburg und München. Pixelgenaue Templates, valides HTML, barrierearm. TYPO3-Projekte, Extension-Programmierung und mehr ... 

Datenbankabfragen sollten in Extensions nie über die normalen SQL-Befehle erfolgen. Gründe:

  • die globale Datenbank-Abstraktionsschicht DBAL wird eingbezogen; heisst, die Extension läuft automatisch auch mit anderen Datenbanken als MySQL
  • die Verbindung zur Datenbank muss nicht eigens erstellt und beendet werden
  • die INSERT-Queries führen automatisch ein FullQuote durch (die Mindest-Absicherung gegen SQL-Injection)
  • die verfügbaren Befehle sind zum Teil einfach bequem …

Funktionen aufrufen

Die Klassen müssen nicht initialisiert werden, es reicht, sie über die GLOBALS aufzurufen:

  1. $GLOBALS['TYPO3_DB']->exec_SELECTquery( .... )

Absicherung gegen SQL-Injection

Alle Eingaben, die vom Besucher der Website kommen (also über GET oder POST) müssen vorher abgesichert werden. Das betrifft vor allem die Befehle zum Einfügen in die Datenbank, ausserdem aber auch die Übernahme von Benutzerdaten in die where-Abschnitte der SQL-Abfragen. Auch hier sollten die Typo3-Funktionen benutzt werden, damit die Quotierung für jede Datenbank passend erfolgt. Folgende Funktionen stehen zur Verfügung:

  1. # Quotieren von Zeichenketten
  2. $GLOBALS['TYPO3_DB']->fullQuoteStr($str,$table);

Parameter

  • string: Zeichenkette, die quotiert werden soll
  • string: Tabellenname (daraus entnimmt DBAL die korrekte Quotierungstechnik)

  1. # Quotieren von eindimensionalen Arrays:
  2. $GLOBALS['TYPO3_DB']->fullQuoteArray($arr,$table,$noQuote=FALSE);

Parameter

  • array: eindimensionales oder assoziatives Array mit den Daten
  • string: Tabellenname (daraus entnimmt DBAL die korrekte Quotierungstechnik)
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen. Nur bei assoziativen Datenarrays anwenden!

  1. # kommaseparierte Listen zu Integerlisten umwandeln (über intval() )
  2. $GLOBALS['TYPO3_DB']->cleanIntList($list)

Parameter

  • string: kommaseparierte Liste mit Werten, die Integer sein sollen

  1. # Arraywerte zu Integer umwandeln (über intval() )
  2. $GLOBALS['TYPO3_DB']->cleanIntArray($arr)

Parameter

  • array: Array mit Werten, die Integer sein sollen

Datenbankabfragen

  1. # SELECT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  3.       $select_fields,
  4.       $from_table,
  5.       $where_clause,
  6.       $groupBy='',
  7.       $orderBy='',
  8.       $limit=''
  9. )

Parameter

  • string: Liste der Felder oder * für alle Felder
  • string: Tabelle(n). Es gelten die üblichen SQL-Regeln für Aliase
  • string: WHERE-Klausel. Wie in SQL üblich. ACHTUNG: man muss alle GET / POST-Werte hier selbst qotieren! Verwenden Sie $this->fullQuoteStr() – siehe oben. Hier nicht Befehle wie GROUP oder LIMIT verwenden.
  • string: Optionale GROUP-Anweisung
  • string: Optionale ORDER BY-Anweisung
  • string: Optionale LIMIT-Anweisung.

  1. # INSERT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_INSERTquery    (
  3.       $table,
  4.       $fields_values,
  5.       $no_quote_fields=FALSE
  6. )

Parameter

  • string: Tabelle
  • array: Feldwerte als array mit key=>value Paaren. Die Werte werden intern quotiert. Typischerweise verwenden Sie ein Array “$insertFields” mit ‘fieldname’=>‘value’ und übergeben es als Argument.
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen; siehe ober bei fullQuoteArray()

  1. # UPDATE-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_UPDATEquery    (
  3.       $table,
  4.       $where,
  5.       $fields_values,
  6.       $no_quote_fields=FALSE
  7. )

Parameter

  • string: Tabelle
  • string: WHERE-Anweisung, typischerweise “uid=xx”. Achtung: Sie müssen alle GET- oder POST-Parameter hier selbst quoten – siehe oben.
  • array: Feldwerte als array mit key=>value Paaren. Die Werte werden intern quotiert. Typischerweise verwenden Sie ein Array “$insertFields” mit ‘fieldname’=>‘value’ und übergeben es als Argument.
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen; siehe ober bei fullQuoteArray()

  1. # DELETE-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_DELETEquery($table,$where)

Parameter

  • string Tabelle
  • string WHERE-Anweisung, typischerweise “uid=xx”. Achtung: Sie müssen alle GET- oder POST-Parameter hier selbst quoten – siehe oben.

Spezielle Datenbank-Anweisungen

Die folgende Funktion eignet sich hervorragend zum Auswerten einer n-n Datenverbindung, wie sie zum Beispiel bei der Verwendung von Kategorien anfällt. Bedingung ist allerdings, dass die Verbindung mit einer m_m-Tabelle erfolgt und nicht über ein einzelnes Feld mit kommaseparierter ID-Liste.

  1. # SELECT relational
  2. $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
  3.       $select,
  4.       $local_table,
  5.       $mm_table,
  6.       $foreign_table,
  7.       $whereClause='',
  8.       $groupBy='',
  9.       $orderBy='',
  10.       $limit=''
  11. )

Parameter

  • string: Liste der Felder oder * für alle Felder
  • string: Name der lokalen Tabelle
  • string: Name der Relationalen m_m-Tabelle
  • string: Name der fremden, verknüpften Tabelle
  • string: WHERE-Klausel. Wie in SQL üblich. ACHTUNG: man muss alle GET / POST-Werte hier selbst qotieren! Verwenden Sie $this->fullQuoteStr() – siehe oben. Hier nicht Befehle wie GROUP oder LIMIT verwenden. Es muss ein ‘ AND ‘ eingefügt werden.
  • string: Optionale GROUP-Anweisung
  • string: Optionale ORDER BY-Anweisung
  • string: Optionale LIMIT-Anweisung.

Achtung: Viele Feldnamen in Typo3 sind identisch (uid, pid, hidden etc.). Das kann bei der Auswertung zu Problemen führen, da die Felder nicht eindeutig sind! Es ist deshalb sinnvoll, imm select- und für die Tabellennamen mit Aliasen zu arbeiten:

  1. $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
  2.       'a.uid as a_uid, b.uid as b_uid',
  3.       'tabelle_1 a',
  4.       'tabelle_m_m',
  5.       'tabelle_2 b',
  6.       $whereClause='',
  7.       $groupBy='',
  8.       $orderBy='',
  9.       $limit=''
  10. )

Ein ganz besondere Funktion ist listQuery(): Sie ermöglicht eine einfache Behandlung von relationalen Verknüpfungen, die nicht über eine dritte m_m-Tabelle laufen sondern über Felder mit kommaseparierten Listen.

  1. $GLOBALS['TYPO3_DB']->listQuery  (
  2.       $field,
  3.       $value,
  4.       $table
  5. )

Das Problem derartiger Verknüpfungen ist, dass die Abfrage über LIKE laufen muss, und dabei zusätzlich unterschieden werden muss, auf welcher Seite des gewünschten Wertes das Komma folgt. Kern der Funktion ist diese Zeile:

  1. $where='('.$field.' LIKE \'%,'.$command.',%\' OR '.$field.' LIKE \''.$command.',%\' OR '.$field.' LIKE \'%,'.$command.'\' OR '.$field.'=\''.$command.'\')';

Parameter

  • string: Feldname, der die kommaseparierte Liste enthält
  • string: Wert, der gefunden werden soll
  • string: Tabelle, in der gesucht wird (für die korrekte Behandlung durch DBAL)

In die gleiche Richtung geht die folgende Funktion, die Srings für die LIKE-Abfrage korrekt für DBAL umsetzt:

  1. $GLOBALS['TYPO3_DB']->escapeStrForLike($str,$table)

WICHTIG: Es gibt eine hervorragende Abkürzung, um die wichtigen einschränkenden Felder in der WHERE-Abfrage automatisch einzuschließen: hidden, deleted, von/bis, Frontenduser etc. Die WHERE-Abrage muss einfach damit erweitert werden:

  1. $this->cObj->enableFields('datenbank')

Also zum Beispiel in der exec_SelectQuery:

  1. # SELECT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  3.       $select_fields,
  4.       $from_table,
  5.       $where_clause.$this->cObj->enableFields('datenbank'),
  6.       $groupBy='',
  7.       $orderBy='',
  8.       $limit=''
  9. )

Noch drei weitere Funktionen werden öfter gebraucht:

  1. # Liefert eine Integer zurück mit der Anzahl der betroffenen Datenzeilen, z. B. bei INSERT oder UPDATE
  2. $GLOBALS['TYPO3_DB']->sql_affected_rows( )

  1. # Liefert eine Integer, die uid des zuletzt eingefügten Datensatzes (über INSERT)
  2. $GLOBALS['TYPO3_DB']->sql_insert_id  ( )

  1. # Liefert eine Integer, mit der Anzahl der gefundenen Datensätze
  2. $GLOBALS['TYPO3_DB']->t3lib_DB.sql_num_rows($res)

Parameter

  • pointer: Der Resultpointer einer vorher erfolgten SELECT-Abfrage.

Weiterführende Links

 

Kategorien: API/Extensions  
Links:
Trackback-Link  (Bitte kopieren)
 
| Mehr
  •  
  • Kommentare
  •  
Gravatar: SK
SK am
26Juni2011

Hi, wie und wo gebe ich denn JOINS bei einer Select Abfrage an? Müssen die mit in die Where Klausel oder wo werden die reingepackt? Mfg und Danke.

Gravatar: peter
peter am
27Juni2011

He he, gute Frage :-) Ich muss zugeben, dass ich es oft im "old-style" baue: »WHERE table1.uid = table2.pid". Dann muss es natürlich in den where-Teil. JOIN muss aber syntaktisch vor WHERE stehen, also muss es wohl in $from_table rein. Aber ungetestet ...

Gravatar: SK
SK am
27Juni2011

Hi, danke. Werde ich nachher mal testen. eine Frage noch. Wie kann ich denn DISTINCT einbauen. Das einfach in den SELECT_FIELDS Teil klappt leider nicht. Danke.

Gravatar: peter
peter am
27Juni2011

Müßte aber eigentlich schon. Die query wird letztlich so aufgebaut: $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table .(strlen($where_clause) > 0 ? ' WHERE ' . $where_clause : ''); $select_fields wird weiter nicht geprüft, müsste also gehen, etwa so: 'DISTINCT table.field1'

Gravatar: SK
SK am
28Juni2011

Guten Morgen, also ich bekomme es einfach nicht zum Laufen. Wenn Du heute evtl. noch mal Zeit und Lust hast, gucke Dir doch bitte mal meine normale Abfrage an und baue Die so wie Du denkst um. Das wäre super nett. Hier mal meine normale Datenbankabfrage: $sql = ' SELECT DISTINCT tx_tl24_subcategories.uid,tx_tl24_subcategories.subcategory_de,tx_tl24_subcategories.subcategory_en FROM tx_tl24_subcategories INNER JOIN tx_tl24_listings ON ( tx_tl24_listings.subcategory = tx_tl24_subcategories.uid ) WHERE '; if($this->langId == 0) $sql .= 'tx_tl24_subcategories.subcategory_de LIKE "'.mysql_real_escape_string($searchword).'%" '; if($this->langId == 1) $sql .= 'tx_tl24_subcategories.subcategory_en LIKE "'.mysql_real_escape_string($searchword).'%" '; $sql.= 'AND tx_tl24_listings.hidden = 0 AND tx_tl24_listings.deleted = 0 AND tx_tl24_subcategories.hidden = 0 AND tx_tl24_subcategories.deleted = 0 ORDER BY tx_tl24_subcategories.subcategory_de ASC'; DANKE

Gravatar: SK
SK am
28Juni2011

Hi, die erfreuliche Nachricht. Ich habe es hinbekommen. Läuft super. Mir kommt es vor als ob die Seite mit den Typo3 Datenbankfunktionen irgendwie schneller läuft. Kann das sein? Eine Frage aber noch: Bei der Update Funktion. Dort werden die Felder und Werte ja als Array übergeben: fieldname=>value. Ich habe nun als fieldname views. Und bei value möchte ich nun gerne das das Feld views immer um 1 erhöht wird. Hatte es vorher halt so: SET views = views+1. Das geht so aber nicht. Hast Du noch einen rettenden Tipp?

Gravatar: SK
SK am
29Juni2011

Hi, läuft alles super. Vielen Dank. Aber nochmal was anderes: Dies ist mein "Alter-SQL-WHERE-TEIL": $queryParts['WHERE'].= 'tx_tl24_companies.company_name LIKE \''.mysql_real_escape_string($searchword).'\''; Wie muss der umgebaut werden? Verwende ich hier fullQuoteStr($str,$table)? Wenn ja, wie? Und warum? Oder verwende ich hier escapeStrForLike($str,$table)? Wenn ja, wie? Und warum? Danke für die Hilfe...

Gravatar: Peter
Peter am
29Juni2011

Wenn du dir die Funktionen anschaust, wird klar, was passiert: quoteStr($str, $table) { return mysql_real_escape_string($str, $this->link); } function escapeStrForLike($str, $table) { return addcslashes($str, '_%'); } Absichern gegen SQL-Injection tun sie beide.

Gravatar: Stefan Schütt
Stefan Schütt am
23August2011

Hallo und erst mal vielen Dank für die sehr gute Übersicht! Ich bin über den Parameter $no_quote_fields gestolpert, und da ich erst nach intensiverem Googlen fündig geworden bin, hier die Info für alle anderen Suchenden: Standardmäßig ist dieser Parameter in den Insert- und Update-Statements auf "false" gesetzt, was bedeutet, dass alle Werte gequotet (und damit vor SQL-Injection geschützt) werden. Man kann den Parameter also auch einfach weglassen, wenn man dieses Verhalten möchte. Will man einzelne Felder vom Quoting ausnehmen, so übergibt man in $no_quote_fields ein Array mit den Namen der betreffenden Felder.

Mein Kommentar

Benachrichtige mich, wenn jemand einen Kommentar zu dieser Nachricht schreibt.

Besuchen Sie mich auf Google+