Hauptmenü

Untermenü

Advanced SQL - Praxistutorial 1 - Tauschangebot machen Teil 3

1. Die Abschnitte

2. Die Methode transactExchangeOffer

... sieht erst mal ziemlich pupsig aus, hat es aber in sich. Außerdem gibt es eine Premiere, denn wir werden in diesem Tutorial zum ersten Mal mit einer halbwegs vernünftigen Fehlerbehandlung arbeiten. Werfen wir zunächst einen Blick auf den grundsätzlichen Aufbau.


public function transactExchangeOffer($queries$settings) {
  try { 
    $this -> beginTransaction();  
    ...
    $this -> commit();
  } 
  catch(PDOExecption $e) {
    $this -> rollback();
  } 
}  

Erläuterung

Im try-Block wird zuerst eine Transaktion gestartet. Dann kommen die eigentlichen Queries (im Moment das ...). Die werden dann per commit dauerhaft in die Datenbank geschrieben. Aber was ist, wenn irgendwas in die Hose geht? Nun, dann wird im catch-Block einfach ein Rollback gestartet und nichts passiert.

3. Die Queries

Wo bis dato nur die drei Pünktchen standen, gehört der folgende Code hin.


if ($this -> insert($queries['angebot'], $settings['angebot'])) {
  $insert_id $this -> lastInsertId();
  for ($i 0$i count($settings['mitglied']); $i++) {
    $settings['mitglied'][$i][':angebot'] = 
      sprintf($settings['mitglied'][$i][':angebot'], $insert_id); 
    $this->insert($queries['tausch'], $settings['mitglied'][$i]);  
  }
}

Erläuterung

Zunächst tragen wir die Daten für die Tabelle angebot ein ($this -> insert($queries['angebot'], $settings['angebot'])). Warum in einer Bedingung? Ganz einfach, wenn alles geklappt hat, so gibt execute ein true zurück. Dann holen wir uns mit der PDO-Methode $this -> lastInsertId() die ID des Datensatzes.

Dann wird das Array $settings['mitglied'] durchlaufen. Und jetzt kommt die Sache mit dem %d. Der ist wie schon gesagt ein Platzhalter und wird dann mit der PHP-eigenen Methode sprintf durch den Wert der Variable $insert_id ersetzt. Zu guter Letzt werden dann die Werte in die Tabelle tausch geschrieben.

4. Aber

Wenn ihr jetzt in dem Formular ein paar Mitglieder auswählt und auf den Submit-Button klickt, fliegt euch auf einmal eine dicke Fehlermeldung um die Ohren.


Fatal errorUncaught exception 'PDOException' 
with message 'SQLSTATE[23000]: Integrity constraint violation: 
1452 ... a foreign key constraint fails 

Dumm gelaufen

Was zum Teufel ist denn jetzt los? Wo passt was nicht und warum? Leider ist obige Fehlermeldung nicht sehr aussagekräftig. Jetzt könnte ich euch bis zum Sankt-Nimmerleins-Tag suchen lassen. Oder ich verrate es euch.

5. Das Problem

Schaut euch mal genau den SQL-Dump an, wo es um die Tabelle angebot geht. Dämmert schon was? Nun dort gibt es folgenden Eintrag.


FOREIGN KEY (`fuer`) REFERENCES `mitglied` (`id`) 
  ON DELETE CASCADE ON UPDATE CASCADE

In der Spalte fuer muss also ein Eintrag stehen, der in der Tabelle mitglied vorkommt. Wir allerdings wollen Dank unseres üblen Datenmodells eine 0 hineinschreiben. Und das kann nicht klappen, weil es in der mitglied keinen entsprechenden Wert bei der ID gibt.

Und nun?

Da es sich hier um ein Tutorial handelt, könntet ihr den Fremdschlüssel aus dem Dump entfernen und ihn neu einspielen. Aber was ist, wenn der Fehler euch erst auffällt, nachdem schon zig tausend Eintrage in die Datenbank geschrieben wurden und dort auch bleiben sollen?

6. Die Lösung

Dafür reicht eine einfache SQL-Anweisung aus, die prinzipiell so aussieht.


ALTER TABLE angebot DROP FOREIGN KEY [symbol];

[symbol]???

Das ist jetzt ein wenig kompliziert. Also MySQL verwaltet Fremdschlüssel über das schon genannte Symbol. Dabei handelt sich um eine Art von interner Namensvergabe. Die Vergabe läuft nach dem Schema tabellenname_ibfk_fortlaufenden_nummer ab. Zumindest bei mir.

Aber wie

... kommt man an den Namen ran? Da gibt es zum Beispiel die Möglichkeit über ein SHOW CREATE TABLE angebot;. Dann bekommt man in der Spalte Create_Table das Symbol aufgelistet. Wenn man es findet. Oder man exportiert in phpMyAdmin/HeidiSQL die Tabellenstruktur und schaut sich das Ergebnis an. Oder man greift auf die Tabelle key_column_usage der Datenbank information_schema zu. Vorausgesetzt, man hat die entsprechenden Rechte.


SELECT 
    CONSTRAINT_NAME
FROM 
    information_schema.key_column_usage 
WHERE CONSTRAINT_NAME LIKE 'angebot%'
    AND TABLE_NAME 'angebot'
    AND COLUMN_NAME 'fuer'

Egal wie ihr vorgeht, der Name von [symbol] lautet angebot_ibfk_3. Den kann man nun ganz einfach plätten und es klappt auch wieder. Probiert es aus.


ALTER TABLE angebot DROP FOREIGN KEY angebot_ibfk_3;

7. Fazit

Ich hoffe, ihr habt ein paar Dinge über PDO, Views, Prepared Statements, Transaktionen und Fremdschlüssel gelernt. Im kommenden Tutorial werden wir dann das Niveau ein wenig steigern und uns weiter vorarbeiten.

zurück zum vorherigen Abschnitt