Transaksi ORM Laravel Eloquent

96

ORM yang fasih cukup bagus, meskipun saya bertanya-tanya apakah ada cara mudah untuk mengatur transaksi MySQL menggunakan innoDB dengan cara yang sama seperti PDO, atau apakah saya harus memperluas ORM untuk memungkinkannya?

wesside
sumber

Jawaban:

165

Kamu bisa melakukan ini:

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

Segala sesuatu di dalam Penutupan dijalankan dalam transaksi. Jika pengecualian terjadi, ia akan melakukan rollback secara otomatis.

Laurence
sumber
1
Di dalam closure saya bisa memanggil query di kelas? Ini akan berhasil?
Rafael Soufraz
Sayangnya itu tidak berfungsi untuk saya jika saya membuat contoh model berbeda yang menyimpan catatan dalam metode mereka sendiri yang relevan.
Volatil3
Jika saya menemukan pengecualian di dalam transaksi saya (untuk menghasilkan pesan kesalahan, dll.), Apakah saya perlu mengeluarkan kembali pengecualian agar rollback terjadi?
alexw
3
Jawaban yang bagus tetapi beberapa hal menarik perhatian saya: 1. Anda perlu menambahkan "use DB;" untuk melakukan ini misalnya di bagian atas file model Anda 2. Tidak seperti JS, Anda tidak mendapatkan akses ke variabel lokal dalam lingkup induk kecuali Anda secara eksplisit meneruskannya, Anda perlu menambahkan konstruksi "gunakan" dengan demikian ... DB :: transaction (function () use ($ user) {... barang yang merujuk $ user ...});
Polsonby
Discussed in more detail herelink sudah mati.
tomloprod
100

Jika Anda tidak menyukai fungsi anonim:

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

Pembaruan : Untuk laravel 4, pdoobjeknya tidak lagi publik jadi:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Jürgen Paul
sumber
15
Anda juga dapat menggunakan metode pintasan DB::beginTransaction()& DB::commit()& DB::rollback(). Itu akan sedikit lebih bersih.
Flori
2
Perbarui untuk menggunakan saran @Flori. Itu lebih bersih. Selain itu, memindahkan jawaban baru ke atas akan membuat jawaban Anda tidak terlalu membingungkan. Saya menggunakan metode pertama sebelum kembali untuk yang kedua.
frostymarvelous
Untuk versi Laravel yang lebih lama, Anda mungkin memerlukan:DB::connection()->getPdo()->beginTransaction();
sebagai gantinya
Saya pribadi berpikir DB::transactiondengan callback bahkan lebih bersih tetapi kekurangannya adalah jika Anda perlu menentukan penangan yang berbeda untuk pengecualian yang berbeda, Anda harus kembali untuk mencoba / menangkap teknik
OzzyTheGiant
33

Jika Anda ingin menggunakan Eloquent, Anda juga dapat menggunakan ini

Ini hanya contoh kode dari proyek saya

        /* 
         * 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
sumber
The question->idekspresi di balik transaksi kembali nol.
Christos Papoulas
@ChristosPapoulas maksud Anda, kami tidak bisa mendapatkan auto increment id dalam transaksi?
hellojinjie
27

Jika Anda ingin menghindari penutupan, dan senang menggunakan fasad, hal-hal berikut ini menjaga agar tetap bagus dan bersih:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Jika ada pernyataan yang gagal, komit tidak akan pernah berhasil, dan transaksi tidak akan diproses.

Chris
sumber
Jika ada pernyataan yang gagal, pernyataan berikutnya tidak akan berjalan. Anda masih perlu membatalkan transaksi secara eksplisit.
Jason
1
@Jason saya telah memperbarui jawabannya. Saya memiliki dua pikiran tentang jika saya harus, untuk sebagian besar (semua?) Mesin database, ketika koneksi diakhiri, setiap pertanyaan transaksional yang tidak dilakukan tidak akan dilakukan. Namun, saya setuju dengan apa yang Anda katakan, dan mungkin yang terbaik adalah secara eksplisit
Chris
19

Saya yakin Anda tidak mencari solusi penutupan, coba ini untuk solusi yang lebih ringkas

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}
imal hasaranga perera
sumber
10

Untuk beberapa alasan cukup sulit untuk menemukan informasi ini di mana pun, jadi saya memutuskan untuk mempostingnya di sini, karena masalah saya, sementara terkait dengan transaksi Eloquent, benar-benar mengubah ini.

Setelah membaca jawaban stackoverflow INI , saya menyadari tabel database saya menggunakan MyISAM, bukan InnoDB.

Agar transaksi dapat berfungsi di Laravel (atau di mana pun yang terlihat), tabel Anda harus diatur untuk menggunakan InnoDB

Mengapa?

Mengutip dokumen Transaksi MySQL dan Operasi Atom (di sini ):

MySQL Server (versi 3.23-max dan semua versi 4.0 dan yang lebih baru) mendukung transaksi dengan mesin penyimpanan transaksional InnoDB dan BDB. InnoDB memberikan kepatuhan ACID penuh. Lihat Bab 14, Mesin Penyimpanan. Untuk informasi tentang perbedaan InnoDB dari SQL standar terkait dengan penanganan kesalahan transaksi, lihat Bagian 14.2.11, “Penanganan Kesalahan InnoDB”.

Mesin penyimpanan nontransaksional lainnya di Server MySQL (seperti MyISAM) mengikuti paradigma berbeda untuk integritas data yang disebut "operasi atom". Dalam istilah transaksional, tabel MyISAM secara efektif selalu beroperasi dalam mode autocommit = 1. Operasi atom sering menawarkan integritas yang sebanding dengan kinerja yang lebih tinggi.

Karena Server MySQL mendukung kedua paradigma tersebut, Anda dapat memutuskan apakah aplikasi Anda paling baik dilayani oleh kecepatan operasi atom atau penggunaan fitur transaksional. Pilihan ini dapat dibuat per tabel.

dmmd
sumber
Ini benar untuk DML, dan tidak selalu benar untuk DDL.
Yevgeniy Afanasyev
4

Jika terjadi pengecualian, transaksi akan dibatalkan secara otomatis.

Format transaksi Laravel Basic

    try{
    DB::beginTransaction();

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


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

    DB::rollback();
    /* Transaction failed. */
}
srmilon.dll
sumber