Transacciones Eloquent ORM de Laravel

96

El ORM de Eloquent es bastante bueno, aunque me pregunto si hay una manera fácil de configurar transacciones MySQL usando innoDB de la misma manera que PDO, o si tendría que extender el ORM para que esto sea posible.

Wesside
fuente

Respuestas:

165

Puedes hacerlo:

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

Todo dentro del cierre se ejecuta dentro de una transacción. Si ocurre una excepción, se revertirá automáticamente.

Laurence
fuente
1
Dentro del cierre puedo llamar consultas en una clase? ¿Funcionará?
Rafael Soufraz
Lamentablemente, no me funciona si estoy creando una instancia de diferentes modelos que están almacenando registros en sus propios métodos relevantes.
Volatil3
Si detecto una excepción dentro de mi transacción (para generar mensajes de error, etc.), ¿necesito volver a emitir la excepción para que se produzca la reversión?
alexw
3
Buena respuesta, pero un par de cosas me sorprendieron: 1. Necesita agregar "use DB;" para hacer esto, por ejemplo, en la parte superior de su archivo de modelo 2. A diferencia de JS, no obtiene acceso a las variables locales en el alcance principal a menos que las pase explícitamente, debe agregar la construcción "use" de esta manera ... DB :: transacción (función () uso ($ usuario) {... cosas que hacen referencia a $ usuario ...});
Polsonby
Discussed in more detail hereel enlace está muerto.
tomloprod
100

Si no le gustan las funciones anónimas:

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

Actualización : para laravel 4, el pdoobjeto ya no es público, así que:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Jürgen Paul
fuente
15
También puede utilizar los métodos de acceso directo DB::beginTransaction()& DB::commit()& DB::rollback(). Eso sería un poco más limpio.
Flori
2
Actualice para utilizar la sugerencia de @Flori. Está más limpio. Además, mover la nueva respuesta hacia arriba hará que su respuesta sea menos confusa. Usé el primer método antes de volver por el segundo.
frostymarvelous
Para una versión anterior de Laravel, es posible que necesite:DB::connection()->getPdo()->beginTransaction();
lugar de
Personalmente, creo que la DB::transactiondevolución de llamada con es aún más limpia, pero el inconveniente es que si necesita especificar diferentes controladores para diferentes excepciones, tendrá que volver a intentar / atrapar la técnica
OzzyTheGiant
33

Si desea utilizar Eloquent, también puede utilizar este

Este es solo un código de muestra de mi proyecto

        /* 
         * 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
fuente
La question->idexpresión en la devolución de llamada de la transacción devuelve cero.
Christos Papoulas
@ChristosPapoulas ¿quiso decir, no podemos obtener la identificación de incremento automático en la transacción?
hellojinjie
26

Si desea evitar los cierres y está feliz de usar fachadas, lo siguiente mantiene las cosas agradables y limpias:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Si alguna declaración falla, la confirmación nunca llegará y la transacción no se procesará.

Chris
fuente
Si alguna declaración falla, las declaraciones posteriores no se ejecutarán. Aún necesita revertir explícitamente la transacción.
Jason
1
@Jason He actualizado la respuesta. Estaba en dos mentes sobre si debería, para la mayoría (¿todos?) Los motores de base de datos, cuando se termina la conexión, las consultas transaccionales no confirmadas no se confirmarán. Sin embargo, estoy de acuerdo con lo que está diciendo, y probablemente sea mejor ser explícito
Chris
18

Estoy seguro de que no está buscando una solución de cierre, pruebe esto para obtener una solución más compacta

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

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

Por alguna razón, es bastante difícil encontrar esta información en cualquier lugar, así que decidí publicarla aquí, ya que mi problema, aunque estaba relacionado con las transacciones de Eloquent, estaba cambiando esto exactamente.

Después de leer ESTO respuesta de stackoverflow, me di cuenta de que las tablas de mi base de datos usaban MyISAM en lugar de InnoDB.

Para que las transacciones funcionen en Laravel (o en cualquier otro lugar, como parece), es necesario que sus tablas estén configuradas para usar InnoDB

¿Por qué?

Citando documentos de transacciones MySQL y operaciones atómicas ( aquí ):

MySQL Server (versión 3.23-max y todas las versiones 4.0 y superiores) admite transacciones con los motores de almacenamiento transaccional InnoDB y BDB. InnoDB proporciona total conformidad con ACID. Consulte el Capítulo 14, Motores de almacenamiento. Para obtener información sobre las diferencias de InnoDB con el SQL estándar con respecto al tratamiento de errores de transacción, consulte la Sección 14.2.11, “Manejo de errores de InnoDB”.

Los otros motores de almacenamiento no transaccionales en MySQL Server (como MyISAM) siguen un paradigma diferente para la integridad de los datos llamado "operaciones atómicas". En términos transaccionales, las tablas MyISAM siempre operan efectivamente en modo autocommit = 1. Las operaciones atómicas a menudo ofrecen una integridad comparable con un mayor rendimiento.

Dado que MySQL Server admite ambos paradigmas, puede decidir si sus aplicaciones se benefician mejor con la velocidad de las operaciones atómicas o con el uso de funciones transaccionales. Esta elección se puede hacer por mesa.

dmmd
fuente
Esto es cierto para DML y no siempre es cierto para DDL.
Yevgeniy Afanasyev
4

Si ocurre alguna excepción, la transacción se revertirá automáticamente.

Formato de transacción básico de Laravel

    try{
    DB::beginTransaction();

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


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

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