Transazioni ORM Eloquenti Laravel


96

L'ORM Eloquente è piuttosto carino, anche se mi chiedo se esiste un modo semplice per configurare le transazioni MySQL utilizzando innoDB allo stesso modo di PDO, o se dovrei estendere l'ORM per renderlo possibile?

Risposte:


165

Puoi farlo:

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

Tutto all'interno della chiusura viene eseguito all'interno di una transazione. Se si verifica un'eccezione, verrà eseguito il rollback automaticamente.


1
All'interno della chiusura posso chiamare query in una classe? Funzionerà?
Rafael Soufraz

Purtroppo non funziona per me se creo istanze di diversi modelli che memorizzano i record nei loro metodi pertinenti.
Volatil3

Se rilevo un'eccezione all'interno della mia transazione (per la generazione di messaggi di errore, ecc.), Devo riemettere l'eccezione per fare in modo che si verifichi il rollback?
alexw

3
Buona risposta, ma un paio di cose mi hanno colto di sorpresa: 1. È necessario aggiungere "usa DB;" per fare ciò, ad esempio all'inizio del file modello 2. A differenza di JS, non hai accesso alle variabili locali nello scope genitore a meno che non le passi esplicitamente, devi aggiungere il costrutto "use" in questo modo ... DB :: transaction (function () use ($ user) {... stuffs referencing $ user ...});
Polsonby

Discussed in more detail hereil collegamento è morto.
tomloprod

100

Se non ti piacciono le funzioni anonime:

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

Aggiornamento : per laravel 4, l' pdooggetto non è più pubblico quindi:

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

15
Puoi anche utilizzare i metodi di scelta rapida DB::beginTransaction()& DB::commit()& DB::rollback(). Sarebbe un po 'più pulito.
Flori

2
Aggiorna per utilizzare il suggerimento di @Flori. È più pulito. Inoltre, spostare la nuova risposta verso l'alto renderà la tua risposta meno confusa. Ho usato il primo metodo prima di tornare per il secondo.
frostymarvelous

Per la versione precedente di Laravel, potresti aver bisogno di:DB::connection()->getPdo()->beginTransaction();
invece

Personalmente penso che DB::transactioncon callback sia ancora più pulito, ma lo svantaggio è che se devi specificare diversi gestori per diverse eccezioni dovrai tornare alla tecnica try / catch
OzzyTheGiant

33

Se vuoi usare Eloquent, puoi usare anche questo

Questo è solo un esempio di codice dal mio progetto

        /* 
         * 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();
        });

L' question->idespressione al callback della transazione restituisce zero.
Christos Papoulas

@ChristosPapoulas volevi dire, non possiamo ottenere l'ID di incremento automatico nella transazione?
hellojinjie

26

Se vuoi evitare chiusure e sei felice di usare le facciate, quanto segue mantiene le cose belle e pulite:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Se qualche istruzione fallisce, il commit non verrà mai eseguito e la transazione non verrà elaborata.


Se qualche istruzione fallisce, le istruzioni successive non verranno eseguite. È comunque necessario eseguire il rollback esplicito della transazione.
Jason

1
@ Jason ho aggiornato la risposta. Ero in due menti se dovessi, per la maggior parte (tutti?) Motori di database, quando la connessione viene terminata, le query transazionali non impegnate non verranno salvate. Tuttavia, sono d'accordo con quello che stai dicendo, e probabilmente è meglio essere esplicito
Chris

18

Sono sicuro che non stai cercando una soluzione di chiusura, prova questa per una soluzione più compatta

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

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

10

Per qualche motivo è abbastanza difficile trovare queste informazioni ovunque, quindi ho deciso di pubblicarle qui, poiché il mio problema, sebbene correlato alle transazioni eloquenti, stava esattamente cambiando questo.

Dopo aver letto QUESTA risposta di stackoverflow, mi sono reso conto che le tabelle del mio database stavano usando MyISAM invece di InnoDB.

Affinché le transazioni funzionino su Laravel (o in qualsiasi altro posto come sembra), è necessario che le tue tabelle siano impostate per utilizzare InnoDB

Perché?

Citando Transazioni MySQL e documenti di operazioni atomiche ( qui ):

MySQL Server (versione 3.23-max e tutte le versioni 4.0 e successive) supporta le transazioni con i motori di archiviazione transazionale InnoDB e BDB. InnoDB fornisce la piena conformità ACID. Vedere il Capitolo 14, Storage Engine. Per informazioni sulle differenze di InnoDB rispetto allo standard SQL per quanto riguarda il trattamento degli errori di transazione, vedere la Sezione 14.2.11, "Gestione degli errori di InnoDB".

Gli altri motori di archiviazione non transazionali in MySQL Server (come MyISAM) seguono un paradigma diverso per l'integrità dei dati chiamato "operazioni atomiche". In termini transazionali, le tabelle MyISAM funzionano sempre efficacemente in modalità autocommit = 1. Le operazioni atomiche offrono spesso un'integrità comparabile con prestazioni più elevate.

Poiché MySQL Server supporta entrambi i paradigmi, puoi decidere se le tue applicazioni sono meglio servite dalla velocità delle operazioni atomiche o dall'uso di funzionalità transazionali. Questa scelta può essere effettuata per tavolo.


Questo è vero per DML e non sempre vero per DDL.
Yevgeniy Afanasyev

4

Se si verifica un'eccezione, la transazione verrà ripristinata automaticamente.

Formato di transazione 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. */
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.