Parameter an eine MySQL-Query übergeben

Es gibt zwei verschiedene Wege, wie man mittels PHP Parameter an eine MySQL-Query übergeben kann.

Der erste Weg gestaltet sich im Prinzip wie folgt:
/* Beispiel 1 */

$query = "SELECT * FROM table WHERE (column1 = '%u') AND (column2 LIKE '%%%s%%');";

Der zweite Ansatz gibt den Parametern eine Reihen- bzw. Rangfolge (auch als argument-numbering bezeichnet):
/* Beispiel 2 */ 

$query = "SELECT * FROM table WHERE (column1 = '%1\$u') AND
                             (column2 LIKE '%%%2\$s%%');";

Die Parameter kann man dann z.B. mittels folgender Methode übergeben:
$res = $db->query( $query, array(10, "Hallo"));

Dabei sollte man die Reihenfolge genau beachten. Im oberen Beispiel 2 übergeben wir zwei verschiedene Parameter (einen ganzzahligen Wert und eine Zeichenkette) an die Positionen '1$' und '2$'. Falsch wäre es nun, wenn man die Position der Zeichenkette z.B. auf '3$' festlegen würde:
/* Beispiel 3 - !!FEHLER IM FORMATSTRING!! */

$query = "SELECT * FROM table WHERE (column1 = '%1\$u') AND
                             (column2 LIKE '%%%3\$s%%');";

Warum sollte man den Ansatz aus Beispiel 2 eigentlich nutzen? Er sieht nicht nur komplexer aus als der Ansatz in Beispiel 1, er ist dazu auch noch um ein paar Zeichen länger. Was ist also der Vorteil?

Als Beispiel möchte ich meine Artikel-Suche nennen, denn hier nutze ich diesen Ansatz und erspare mir damit einige Zeilen zusätzlichen Code.


Beispiel Artikel-Suche

Gibt man im Suchfeld dieser Seite einen Begriff, so wird eine Query gebaut, die (vereinfacht gesagt) in der Artikel-Tabelle die Spalten 'a_titel' (der Titel das Artikels), 'a_tags' (dem Artikel zugeordnete Tags) sowie 'a_inhalt' (den Artikelinhalt) nach dem eingegebenen Begriff durchsucht. Würde ich den ersten Ansatz nutzen, so hätte ich eine Query, die in etwa wie folgt aussieht:
/* Beispiel 4 */

// der Query-Formatstring
$query = "SELECT * FROM table WHERE (a_titel LIKE '%%%s%%') OR
                             (a_tags LIKE '%%%s%%') OR
                             (a_inhalt LIKE '%%%s%%');";

// Formatstring und drei Parameter übergeben sowie die Query ausführen
$res = $db->query( $query, array( $suchbegriff, $suchbegriff, $suchbegriff ));

Wie man in Beispiel 4 sieht, muss man den Suchbegriff dreimal übergeben (je einen für jeden Platzhalter).

Mittels argument-numbering muss man dies nicht, hier reicht die Übergabe eines Arguments:
/* Beispiel 5 */

// der Query-Formatstring
$query = "SELECT * FROM table WHERE (a_titel LIKE '%%%1\$s%%') OR
                             (a_tags LIKE '%%%1\$s%%') OR
                             (a_inhalt LIKE '%%%1\$s%%');";

// Formatstring und einen Parameter übergeben sowie die Query ausführen
$res = $db->query( $query, array( $suchbegriff ));

Nun ist meine Suchfunktion aber doch etwas komplexer als hier dargestellt. Es werden z.B. einige Bedingungen geprüft - z.B. ob Volltextsuche, Suche innerhalb der Kommentare, Suche über allen Kategorien usw. markiert wurde. Die Ergebnisse der Auswertung der Bedingungen haben natürlich Einfuß auf den Query-Formatstring, den man letztlich ausführen möchte. Das Problem war nun, dass ich nicht mehr alle Argumente einfach so durchnummerieren durfte. Folgendes einfachte Beispiel soll dies deutlich machen:
/* Beispiel 6 */

query .= "SELECT ..., SUBSTRING(a_inhalt, 1, %1\$u) AS vorschau FROM t_artikel ... ";
if( $volltext_suche == true )
{
    $query .= "WHERE (MATCH (a_title) AGAINST ('%2\$s' IN BOOLEAN MODE)";
    ...
}
else
{
    $query .= "WHERE ((a_titel LIKE '%%%2\$s%%') OR (a_tags LIKE '%%%2\$s%%')";
    ...
}

if($suche_in_allen_jahren == false)
{
    $query .= "AND YEAR(a_datum) = '%?\$s' ..."; // <-- welche Nummer?
}
if($suche_in_allen_kategorien == false)
{
    $query .= "AND (a_kategorie_id = '%?\$u') ..."; // <-- welche Nummer?
}

Welche Nummer soll ich den beiden unteren markierten Zeilen geben? Dies ist nicht ohne weiteres zu lösen, denn es ist nicht gesagt, dass immer über allen Jahren und in allen Kategorien gesucht werden soll. Beides ist möglich, oder keines von beiden oder jeweils eines von beiden. Eine feste Nummerierung kann man hier nicht anwenden.

Zur Lösung des Problems verwende ich eine Variable, die entsprechend hochgezählt wird:
/* Beispiel 7 */
$i = 1

query .= "SELECT ..., SUBSTRING(a_inhalt, 1, %" . $i++ . "\$u) ...";

// ---> i = 2
if( $volltext_suche == true )
{
    $query .= "WHERE (MATCH (a_title) AGAINST ('" . $i . "\$s' IN BOOLEAN MODE)";
    ...
}
else
{
    $query .= "WHERE ((a_titel LIKE '%%%" . $i . "\$s%%') OR
                 (a_tags LIKE '%%%" . $i . "\$s%%')";
    ...
}
$i++;

// ---> i = 3
if($suche_in_allen_jahren == false)
{
    $query .= "AND YEAR(a_datum) = '%" . $i++ . "\$s' ...";
}
// ---> i = 3 oder i = 4
if($suche_in_allen_kategorien == false)
{
    $query .= "AND (a_kategorie_id = '%" . $i . "\$u') ...";
}


 
 Artikel als Bookmark:
Beitrag wurde bisher 1131 mal geöffnet
RA Zabel/Stolze
 
1
cornfelt
Hi Matze,

du müsstest hierbei noch erwähnen, wie die Methode DB::query() überhaupt funktioniert, also, wie sie die Werte des übergebenen Arrays in den Query integriert.
z.B. die Verwendung von
sprintf($query, $value1, ...)


Aber warum schiebst du die ganzen Werte in ein zusätzliches Array? Du kannst sie doch direkt übergeben, denn so sparst du dir das Anlegen des Arrays und das Wiederauslesen der Werte aus dem Array.

cu cornfelt
 
2
Matthias Sonnenkalb
Hallo Cornfelt,

ja, die Methode DB::query hatte ich leider vergessen anzugeben. Dies soll nun erfolgen und so könnte die Methode implementiert sein:

public function query( $query, $params = '' )
{
    $str = '';

    $ary = array();
    foreach( $params as $param )
    {
        $ary[] = mysqli_real_escape_string( self :: $link, $param );
    }

    $str = vsprintf( $query, $ary );

    return mysqli_query( self :: $link, $str );
}

Hm, ich verwende ein Array, da ich die Parameter in der Methode dann mittels foreach auslesen kann. Sicher hätte ich die Parameter auch so übergeben und dann per func_get_args usw. ermitteln können, aber so gehts auch.

Vielleicht hast Du ja einen besseren Vorschlag? Dann immer her damit - wird dankend angenommen :)

cu Matthias
  • 96 Artikel  (zeigen)
  • 66546 Klicks
  • 56 Kommentare
  • 9 Artikel (2010)  (zeigen)
  • 1855 Klicks (2010)
  • 0 Kommentare (2010)
Freunde / Linkparade (4)