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?

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.


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

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

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.


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

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.


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. */
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.