Laravel Eloquent ORM Transactions

97

Eloquent ORM jest całkiem fajny, chociaż zastanawiam się, czy istnieje łatwy sposób na skonfigurowanie transakcji MySQL przy użyciu innoDB w taki sam sposób jak PDO, czy też musiałbym rozszerzyć ORM, aby było to możliwe?

Wesside
źródło

Odpowiedzi:

166

Możesz to zrobić:

DB::transaction(function() {
      //
});

Wszystko wewnątrz Zamknięcia jest wykonywane w ramach transakcji. Jeśli wystąpi wyjątek, nastąpi automatyczne wycofanie.

Laurence
źródło
1
Wewnątrz zamknięcia mogę wywoływać zapytania w klasie? To zadziała?
Rafael Soufraz
Niestety, nie działa to dla mnie, jeśli tworzę wystąpienia różnych modeli, które przechowują dane w swoich własnych odpowiednich metodach.
Volatil3
Jeśli złapię wyjątek w mojej transakcji (w celu wygenerowania komunikatów o błędach itp.), Czy muszę ponownie wyemitować wyjątek, aby nastąpiło wycofanie?
alexw
3
Dobra odpowiedź, ale kilka rzeczy mnie zaskoczyło: 1. Musisz dodać „użyj DB;” aby to zrobić, np. na górze pliku modelu 2. W przeciwieństwie do JS, nie masz dostępu do zmiennych lokalnych w zakresie nadrzędnym, chyba że jawnie je przekażesz, musisz więc dodać konstrukcję "use" w ten sposób ... DB :: transaction (function () use ($ user) {... rzeczy odwołujące się do $ user ...});
Polsonby
Discussed in more detail herelink jest martwy.
tomloprod
101

Jeśli nie lubisz anonimowych funkcji:

try {
    DB::connection()->pdo->beginTransaction();
    // database queries here
    DB::connection()->pdo->commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::connection()->pdo->rollBack();
}

Aktualizacja : W przypadku laravel 4 pdoobiekt nie jest już publiczny, więc:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Jürgen Paul
źródło
15
Możesz także użyć metod skrótów DB::beginTransaction()& DB::commit()& DB::rollback(). To byłoby trochę czystsze.
Flori
2
Zaktualizuj, aby skorzystać z sugestii @Flori. Jest czystszy. Przesunięcie nowej odpowiedzi w górę sprawi, że będzie ona mniej zagmatwana. Użyłem pierwszej metody przed powrotem do drugiej.
mroźno cudowny
W przypadku starszej wersji Laravel możesz potrzebować:DB::connection()->getPdo()->beginTransaction();
zamiast tego
Osobiście uważam, że funkcja DB::transactionwith callback jest jeszcze czystsza, ale wadą jest to, że jeśli musisz określić różne
programy
33

Jeśli chcesz używać Eloquent, możesz również użyć tego

To tylko przykładowy kod z mojego projektu

        /* 
         * Saving Question
         */
        $question = new Question;
        $questionCategory = new QuestionCategory;

        /*
         * Insert new record for question
         */
        $question->title = $title;
        $question->user_id = Auth::user()->user_id;
        $question->description = $description;
        $question->time_post = date('Y-m-d H:i:s');

        if(Input::has('expiredtime'))
            $question->expired_time = Input::get('expiredtime');

        $questionCategory->category_id = $category;
        $questionCategory->time_added = date('Y-m-d H:i:s');

        DB::transaction(function() use ($question, $questionCategory) {

            $question->save();

            /*
             * insert new record for question category
             */
            $questionCategory->question_id = $question->id;
            $questionCategory->save();
        });
Aditya Kresna Permana
źródło
question->idWyraz w zwrotnego transakcji zwraca zero.
Christos Papoulas
@ChristosPapoulas czy chodziło Ci o to, że nie możemy uzyskać identyfikatora automatycznego zwiększania wartości w transakcji?
hellojinjie
29

Jeśli chcesz uniknąć zamknięć i chętnie używać fasad, następujące elementy pozwalają zachować ład i czystość:

try {
    \DB::beginTransaction();

    $user = \Auth::user();
    $user->fill($request->all());
    $user->push();

    \DB::commit();

} catch (Throwable $e) {
    \DB::rollback();
}

Jeśli jakiekolwiek instrukcje zawiodą, zatwierdzenie nigdy nie trafi, a transakcja nie zostanie przetworzona.

Chris
źródło
Jeśli jakiekolwiek instrukcje zawiodą, kolejne instrukcje nie będą działać. Nadal musisz wyraźnie wycofać transakcję.
Jason
1
@Jason Zaktualizowałem odpowiedź. Zastanawiałem się, czy powinienem, w przypadku większości (wszystkich?) Silników baz danych, po zerwaniu połączenia żadne zapytania transakcyjne, które nie zostały zatwierdzone, nie zostaną zatwierdzone. Jednak zgadzam się z tym, co mówisz, i prawdopodobnie najlepiej jest to wyrazić wprost
Chris,
19

Jestem pewien, że nie szukasz rozwiązania do zamykania, wypróbuj to, aby uzyskać bardziej kompaktowe rozwiązanie

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}
imal hasaranga perera
źródło
10

Z jakiegoś powodu dość trudno jest znaleźć te informacje w dowolnym miejscu, więc zdecydowałem się je tutaj zamieścić, ponieważ mój problem, związany z transakcjami Eloquent, dokładnie to zmieniał.

Po przeczytaniu TEGO odpowiedzi na stos, zdałem sobie sprawę, że moje tabele bazy danych używają MyISAM zamiast InnoDB.

Aby transakcje działały na Laravel (lub gdziekolwiek indziej, jak się wydaje), wymagane jest, aby twoje tabele korzystały z InnoDB

Czemu?

Cytowanie dokumentów dotyczących transakcji MySQL i Atomic Operations ( tutaj ):

Serwer MySQL (wersja 3.23-max i wszystkie wersje 4.0 i nowsze) obsługuje transakcje z silnikami pamięci transakcyjnej InnoDB i BDB. InnoDB zapewnia pełną zgodność z ACID. Patrz rozdział 14, Silniki pamięci masowej. Aby uzyskać informacje na temat różnic między InnoDB a standardowym SQL w zakresie obsługi błędów transakcji, zobacz Rozdział 14.2.11, „Obsługa błędów InnoDB”.

Inne nietransakcyjne silniki pamięci masowej w MySQL Server (takie jak MyISAM) stosują inny paradygmat integralności danych, zwany „operacjami atomowymi”. Pod względem transakcyjnym tabele MyISAM skutecznie zawsze działają w trybie autocommit = 1. Operacje atomowe często oferują porównywalną integralność z wyższą wydajnością.

Ponieważ MySQL Server obsługuje oba paradygmaty, możesz zdecydować, czy Twoje aplikacje będą najlepiej obsługiwane przez szybkość niepodzielnych operacji, czy wykorzystanie funkcji transakcyjnych. Tego wyboru można dokonać na podstawie tabeli.

dmmd
źródło
Dotyczy to DML, a nie zawsze DDL.
Yevgeniy Afanasjew
4

Jeśli wystąpi jakikolwiek wyjątek, transakcja zostanie automatycznie wycofana.

Laravel Basic format transakcji

    try{
    DB::beginTransaction();

    /* 
    * SQL operation one 
    * SQL operation two
    ..................     
    ..................     
    * SQL operation n */


    DB::commit();
   /* Transaction successful. */
}catch(\Exception $e){       

    DB::rollback();
    /* Transaction failed. */
}
srmilon
źródło