Transaktionen mit AR

Hallo Forenmitglieder,

ich beschäftige mich gerade mit Transaktionen in AR, bekomme das aber irgendwie nicht hin.

Analog zum Beispiel im Guide habe ich folgenden Code ausprobiert:

(für test_id ist im Model integerOnly als rule hinterlegt, Model mit PK 2 existiert in DB)


$model=Site::model();

            $transaction=$model->dbConnection->beginTransaction();

            try {

                $site=$model->findByPk(2);

                echo 1;

                $site->test_id = 'asdf';

                echo 2;

                if(!$site->save()) {

                    echo 'not saved';

                }

                echo 3;

                $transaction->commit();

                echo 4;

            } catch(Exception $e) {

                echo 'Exception!';

            }



Bei der Ausführung wird nie eine Exception geworfen, als Debug-Ausgabe erhalte ich


12not saved34

Was mach ich falsch? Muss ich eine Einstellung in der config beachten?

Wenn ich findByPk auf eine nicht existierende ID Ausführe wird auch keine Exception geworfen, ich bekomme stattdessen einen Fatal error beim save() Aufruf, da $site NULL ist.

Natürlich könnte ich auch den simpleren Weg gehen und $site auf NULL überprüfen und if(!$site->save()) aufrufen, für komplexere Controller mit mehreren beteiligten Models wäre der try catch Ansatz jedoch einfach geschickter.

Viele Grüße,

Chris

Hi Chris und willkommen,

eine Exception wird nur geworfen, wenn es einen Fehler beim Ausführen der SQL-Anweisung gab. Das ist aber hier nicht der Fall. save() liefert aber wohl false zurück, also ging offensichtlich bei der Validierung was schief. Mach doch einfach mal sowas:


if(!$site->save()) {

     //echo 'not saved';

     print_r($site->getErrors());

}

Falls das nichts ausspuckt, wären beforeValidate() und beforeSave() noch Kanditaten. Falls du eine dieser Methoden überschrieben hast, muss diese true zurückgeben, sonst wird das Model nicht gespeichert.

Den Fall, dass $site===null solltest du in jedem Fall abfangen, da du sonst den erwähnten fatal error bekommst. PHP schmeisst leider nicht in jedem Fall eine Exception.

Hallo Mike,

wie ich schon geschrieben hatte könnte ich auch den simpleren Weg gehen und die Werte einzeln überprüfen, das ist mir klar. Ich wollte das Beispiel aus dem Yii Guide nachvollziehen (Kapitel "Transaktionen mit AR") und schauen wo sich das sinnvoll einsetzten lässt.

So wie du auch geschrieben hast dachte ich auch, dass Exceptions nur bei SQL Fehlern geworfen werden. Warum gibt es dann genau das Beispiel für Transaktionen mit AR? In einem Produktivsystem sollten eigentlich keine SQL Fehler mehr auftreten wenn das System ordentlich getestet ist, fehlerhafte POST values können jedoch immer vorkommen.

Angenommen ich hätte 3 verschiedene Models und würde für alle 3 hintereinander save() aufrufen. Beim letzten klappt das save nicht und ich will nun einen Rollback auslösen. Dann kann ich das nicht so machen wie im Beispiel beschrieben, das hab ich verstanden. Was bringt mir das try catch Konzept dann in diesem Zusammenhang? Ich müsste dann ja im Falle if(!$site->save()) selber eine Exception innerhalb des try Blocks werfen …

Ich steh irgendwie auf dem Schlauch.

Viele Grüße,

Chris

Den Rückgabewert einer find*()-Methode solltest du immer prüfen. Es ist kein Fehler, wenn nichts gefunden wird, warum sollte also hier eine Exception ausgelöst werden? find() liefert null, findAll() ein leeres Array bei leerem Suchergebnis zurück.

Bzgl. try/catch-Beispiel:

Gute Frage. :) Ehrlichgesagt bin ich grad auch nicht ganz sicher, ob das Beispiel im Guide gut gewählt ist. Der Kommentar sagt ja:


// find and save are two steps which may be intervened by another request

// we therefore use a transaction to ensure consistency and integrity

Im Beispielzusammenhang seh ich da auch nicht viel Sinn drin. Was sinnvoll gewesen wäre, wäre die Tabelle/den Datensatz zwischen Abfrage und Speichern zu locken. Aber das ist ja was anderes als ne Transaktion.

Sogesehn würd ich mich hier mal nicht zu sehr an den Beispielen aufhängen. Ich würde eher den Rückgabewert von save() prüfen und bei false ein Rollback durchführen. Allerdings auch nur, wenn mehrere verknüpfte Datensätze gespeichert werden sollen. Bei einem einzelnen Datensatz bringt eine Transaktion m.E. recht wenig.

OK, dann sehen wir die Sache gleich. Dann überprüf ich alle Rückgaben wieder wie gehabt "von Hand".

Vielen Dank,

Chris