Transactions ORM éloquentes de Laravel

96

L'ORM Eloquent est assez sympa, même si je me demande s'il existe un moyen simple de configurer des transactions MySQL en utilisant innoDB de la même manière que PDO, ou si je devrais étendre l'ORM pour rendre cela possible?

wesside
la source

Réponses:

165

Tu peux le faire:

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

Tout ce qui se trouve à l'intérieur de la fermeture s'exécute dans une transaction. Si une exception se produit, elle sera annulée automatiquement.

Laurence
la source
1
À l'intérieur de la fermeture, je peux appeler des requêtes dans une classe? Cela fonctionnera?
Rafael Soufraz
Malheureusement, cela ne fonctionne pas pour moi si je crée une instance de différents modèles qui stockent des enregistrements dans leurs propres méthodes pertinentes.
Volatil3
Si j'attrape une exception dans ma transaction (pour générer des messages d'erreur, etc.), dois-je réémettre l'exception pour que l'annulation se produise?
alexw
3
Bonne réponse mais quelques points m'ont attiré: 1. Vous devez ajouter "utiliser DB;" pour le faire, par exemple en haut de votre fichier de modèle 2. Contrairement à JS, vous n'avez pas accès aux variables locales dans la portée parent à moins que vous ne les transmettiez explicitement, vous devez ajouter la construction "use" ainsi ... DB :: transaction (function () use ($ user) {... des trucs faisant référence à $ user ...});
Polsonby
Discussed in more detail herele lien est mort.
tomloprod
100

Si vous n'aimez pas les fonctions anonymes:

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

Mise à jour : pour laravel 4, l' pdoobjet n'est plus public donc:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Jürgen Paul
la source
15
Vous pouvez également utiliser les méthodes de raccourci DB::beginTransaction()& DB::commit()& DB::rollback(). Ce serait un peu plus propre.
Flori
2
Veuillez mettre à jour pour utiliser la suggestion @Flori. C'est plus propre. De plus, déplacer la nouvelle réponse vers le haut rendra votre réponse moins déroutante. J'ai utilisé la première méthode avant de revenir pour la deuxième.
frostymarvelous
Pour une version plus ancienne de Laravel, vous pourriez avoir besoin de:DB::connection()->getPdo()->beginTransaction();
au lieu du
Personnellement, je pense que le DB::transactioncallback est encore plus propre mais l'inconvénient est que si vous devez spécifier différents gestionnaires pour différentes exceptions, vous devrez revenir en arrière pour essayer la technique de capture
OzzyTheGiant
33

Si vous souhaitez utiliser Eloquent, vous pouvez également utiliser ce

Ceci est juste un exemple de code de mon projet

        /* 
         * 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
la source
L' question->idexpression lors du rappel de transaction renvoie zéro.
Christos Papoulas
@ChristosPapoulas voulez-vous dire, nous ne pouvons pas obtenir l'identifiant d'incrémentation automatique dans la transaction?
hellojinjie
26

Si vous voulez éviter les fermetures et que vous préférez utiliser les façades, ce qui suit permet de garder les choses bien et propres:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Si une instruction échoue, la validation ne fonctionnera jamais et la transaction ne sera pas traitée.

Chris
la source
Si des instructions échouent, les instructions suivantes ne seront pas exécutées. Vous devez toujours annuler explicitement la transaction.
Jason
1
@Jason J'ai mis à jour la réponse. Je me demandais si je devais, pour la plupart (tous?) Des moteurs de base de données, lorsque la connexion est terminée, les requêtes transactionnelles non validées ne seront pas validées. Cependant, je suis d'accord avec ce que vous dites, et il vaut probablement mieux être explicite
Chris
18

Je suis sûr que vous ne recherchez pas une solution de fermeture, essayez ceci pour une solution plus compacte

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

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

Pour une raison quelconque, il est assez difficile de trouver ces informations n'importe où, alors j'ai décidé de les publier ici, car mon problème, bien que lié aux transactions Eloquent, changeait exactement cela.

Après avoir lu CETTE réponse stackoverflow, j'ai réalisé que mes tables de base de données utilisaient MyISAM au lieu d'InnoDB.

Pour que les transactions fonctionnent sur Laravel (ou n'importe où ailleurs), il est nécessaire que vos tables soient configurées pour utiliser InnoDB

Pourquoi?

Citant des documents sur les transactions MySQL et les opérations atomiques ( ici ):

MySQL Server (version 3.23-max et toutes les versions 4.0 et supérieures) prend en charge les transactions avec les moteurs de stockage transactionnel InnoDB et BDB. InnoDB fournit une conformité ACID complète. Voir le chapitre 14, Moteurs de stockage. Pour plus d'informations sur les différences d'InnoDB par rapport au SQL standard en ce qui concerne le traitement des erreurs de transaction, voir Section 14.2.11, «Traitement des erreurs InnoDB».

Les autres moteurs de stockage non transactionnels de MySQL Server (tels que MyISAM) suivent un paradigme différent pour l'intégrité des données appelé «opérations atomiques». En termes transactionnels, les tables MyISAM fonctionnent toujours en mode autocommit = 1. Les opérations atomiques offrent souvent une intégrité comparable avec des performances plus élevées.

Étant donné que MySQL Server prend en charge les deux paradigmes, vous pouvez décider si vos applications sont mieux servies par la vitesse des opérations atomiques ou l'utilisation de fonctionnalités transactionnelles. Ce choix peut être fait par table.

dmmd
la source
Cela est vrai pour DML et pas toujours vrai pour DDL.
Yevgeniy Afanasyev
4

Si une exception se produit, la transaction sera annulée automatiquement.

Format de transaction 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
la source