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.

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.


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

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

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á.


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

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.


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. */
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.