dedlfix: Aufbau Datenbankschnittstelle

Beitrag lesen

Hi!

Zahlenwerte können auch als String übergeben werden. Probier mal alle relevanten Typen aus, ob sie sich als s übergeben lassen.
Welchen Nutzen hat es dann überhaupt hier etwas anderes zu verwenden?

Vermutlich kommt das von den typisierten Sprachen. Da kann die Funktion nur generische Typen fordern und man sagt ihr so, was wirklich kommt.

Das Problem ist nicht das Zusammenbauen des SQLs, sondern das übergeben der Paramter an bind().
->bind('s',$fieldValue['first'], $fieldValue['second'], ...)
Ich weiß nicht wie ich die verschiedene Anzahl an Parametern variabel machen könnte.

Ja, das ist eine etwas fiese Geschichte. Da sollen ja unbedingt Variablen und die auch noch per Referenz übergeben werden. Man kann da was mit call_user_func_array() hinbekommen. Bevor ich es lang und breit schreibe, so sieht das bei mir aus:

array_unshift($params, str_repeat('s', count($params)));  
if (!call_user_func_array(array($stmt, 'bind_param'), $params))  
  throw new DbException ...  
// ansonsten weiter mit execute

$params ist ein indexiertes Array, da stecken deine Parameter in der Reihenfolge, wie sie das SQL-Statement haben möchte. Am besten ist es, wenn die Query-Ausführung und damit auch das Binding in einer Funktion gekapselt ist. $params wird als Funktionsparameter übergeben. Die Funktion erhält nun eine Kopie des Arrays. Da das Binding mit Referenzen geschieht, reduzierst du eventuelle Nebenwirkungen, wenn du mit einer dedizierten Kopie arbeitest.

Das sieht jedenfalls noch recht einfach aus. Wenn du jedoch mehrfach exekutieren willst, wird es ungefähr so aufwendig wie beim nachfolgend beschriebenen Fetchen.

Ohne Prepared-Statement wär's kein Problem, aber dann müsste ich mich ja selber um den Kontextwechsel kümmern :-(

Die Frage ist, was am Ende aufwendiger ist. Das Problem des Bindings zu umgehen oder selbst behandeln. Richtig dämlich wird es nämlich erst beim Abfragen des Results. Da musst du nämlich auch wieder mit Binding arbeiten und du willst sicherlich eine variable Anzahl Felder abfragen können. Die erste Schwierigkeit hier ist, erstmal ein Array voller Referenzen zu erzeugen, das man bind_result() übergeben kann:

$values = array();  
foreach ($stmt->result_metadata()->fetch_fields() as $field)  
  $values[$field->name] =& $values[$field->name];  
  
if (!call_user_func_array(array($stmt, 'bind_result'), $values))  
  throw new DbException ...  
// ansonsten fetchen

Die Zeile

$values[$field->name] =& $values[$field->name];

sieht eigentümlich aus, aber das funktioniert und sogar ohne Notice. Beim Erstellen der Referenz findet kein Lesevorgang statt, weswegen die Notice keinen Grund zum Erscheinen hat.

Wenn du das Ergebnis gleich beim Fetchen ausgeben willst, ist mit einfachen Lesezugriffen auf $values alles bestens. Willst du es jedoch erst in einem Array sammeln (natürlich willst du das, denn du willst ja Datenabfrage und Geschäftslogik trennen), dann musst du die referenzierten Werte wieder dereferenzieren. Wenn du das so machst

$rows = array();  
while ($stmt->fetch()) {  
  $rows[] = $values;  
}

zeigt dir ein var_dump($rows); anschließend x-mal den letzten Datensatz, wobei die Elemente immer ein & zum Zeichen der Referenz davorstehen haben.

$rows = array();  
while ($stmt->fetch()) {  
  $row = array();  
  foreach ($values as $key => $value) // dereference bound fetched values  
    $row[$key] = $value;  
  $rows[] = $row;  
}

So wird ein Schuh draus. Und wie du siehst, ist das alles deutlich aufwendiger als Selbstmaskieren, mysqli::query() aufrufen und das zurückgegebene MySQLi_Result-Objekt zu befetchen. PDO ist bei Prepared Statemens übrigens deutlich anwenderfreundlicher.

Zudem werden AFAIK Prepared-Statements effizienter gecachet(?)

Da lies mal lieber die entsprechenden Kapitel im MySQL-Handbuch. Ein Prepared Statement muss zwar nur einmal geparst werden und kann dann beliebig oft exekutiert werden. Aber wenn du nur ein execute() pro Request verwendest, bringt dir das nichts, denn der nächste Request mit neuer Verbindung muss erneut prepare() aufrufen und parsen lassen.

Gute Idee, die Bezeichner zu maskieren, aber falsche Ausführung. Da sind gleich drei Fehler enthalten.
$mysqli->prepare('DELETE FROM '.str_replace('','``',$tableName).' WHERE id = ?');`
So dann richtig?

Ja. Ich würde dir noch sprintf() empfehlen, das sieht meines Erachtens übersichtlicher aus, weil das SQL-Statement zusammenhängend geschrieben werden kann:

$sql = sprintf('DELETE FROM `%s` WHERE id = ?', str_replace('`', '``',$tableName));  
$mysqli->prepare($sql);

Lo!