Transações ORM do Laravel Eloquent

96

O Eloquent ORM é muito bom, embora eu esteja me perguntando se existe uma maneira fácil de configurar transações MySQL usando innoDB da mesma maneira que o PDO, ou se eu teria que estender o ORM para tornar isso possível.

Wesside
fonte

Respostas:

165

Você consegue fazer isso:

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

Tudo dentro do Closure é executado dentro de uma transação. Se ocorrer uma exceção, ele será revertido automaticamente.

Laurence
fonte
1
Dentro do encerramento, posso chamar consultas em uma classe? Vai funcionar?
Rafael Soufraz
Infelizmente, não está funcionando para mim se estou criando instâncias de diferentes modelos que estão armazenando registros em seus próprios métodos relevantes.
Volatil3
Se eu detectar uma exceção dentro da minha transação (para gerar mensagens de erro, etc), preciso reemitir a exceção para que o rollback ocorra?
alexw de
3
Boa resposta, mas algumas coisas me pegaram: 1. Você precisa adicionar "use DB;" para fazer isso, por exemplo, no topo de seu arquivo de modelo 2. Ao contrário de JS, você não obtém acesso às variáveis ​​locais no escopo pai, a menos que você as passe explicitamente, você precisa adicionar a construção "use" assim ... DB :: transação (function () use ($ user) {... coisas que fazem referência a $ user ...});
Polsonby de
Discussed in more detail herelink está morto.
tomloprod
100

Se você não gosta de funções anônimas:

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

Atualização : Para laravel 4, o pdoobjeto não é mais público, então:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Jürgen Paul
fonte
15
Você também pode usar os métodos de atalho DB::beginTransaction()& DB::commit()& DB::rollback(). Isso seria um pouco mais limpo.
Flori
2
Atualize para usar a sugestão @Flori. É mais limpo. Além disso, mover a nova resposta para cima a tornará menos confusa. Usei o primeiro método antes de voltar para o segundo.
frostymarvelous
Para uma versão mais antiga do Laravel, você pode precisar de:DB::connection()->getPdo()->beginTransaction();
vez de
Eu pessoalmente acho que o DB::transactioncallback com é ainda mais limpo, mas a desvantagem é que se você precisar especificar diferentes manipuladores para diferentes exceções, você terá que voltar para a técnica de tentativa / captura
OzzyTheGiant
33

Se você quiser usar o Eloquent, também pode usar este

Este é apenas um exemplo de código do meu projeto

        /* 
         * 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
fonte
A question->idexpressão no retorno de chamada da transação retorna zero.
Christos Papoulas
@ChristosPapoulas você quis dizer que não podemos obter o ID de incremento automático na transação?
hellojinjie
26

Se você quiser evitar fechamentos e ficar feliz em usar fachadas, o seguinte mantém as coisas boas e limpas:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Se alguma instrução falhar, o commit nunca ocorrerá e a transação não será processada.

Chris
fonte
Se alguma instrução falhar, as instruções subsequentes não serão executadas. Você ainda precisa reverter explicitamente a transação.
Jason,
1
@Jason Eu atualizei a resposta. Eu estava em dúvida se deveria, para a maioria (todos?) Dos mecanismos de banco de dados, quando a conexão for encerrada, quaisquer consultas transacionais não confirmadas não serão confirmadas. No entanto, concordo com o que você está dizendo e provavelmente é melhor ser explícito
Chris,
18

Tenho certeza de que você não está procurando uma solução de fechamento, tente uma solução mais compacta

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}
Imal Hasaranga Perera
fonte
10

Por algum motivo, é muito difícil encontrar essas informações em qualquer lugar, então decidi postá-las aqui, pois meu problema, embora relacionado às transações do Eloquent, estava exatamente mudando isso.

Depois de ler ESTA resposta stackoverflow, percebi que minhas tabelas de banco de dados estavam usando MyISAM em vez de InnoDB.

Para que as transações funcionem no Laravel (ou em qualquer outro lugar que pareça), é necessário que suas tabelas sejam configuradas para usar InnoDB

Por quê?

Citando documentos do MySQL Transactions and Atomic Operations ( aqui ):

MySQL Server (versão 3.23-max e todas as versões 4.0 e superiores) oferece suporte a transações com os mecanismos de armazenamento transacional InnoDB e BDB. InnoDB fornece conformidade total com ACID. Consulte o Capítulo 14, Mecanismos de armazenamento. Para obter informações sobre as diferenças entre o InnoDB e o SQL padrão em relação ao tratamento de erros de transação, consulte a Seção 14.2.11, “Tratamento de erros do InnoDB”.

Os outros mecanismos de armazenamento não transacional no MySQL Server (como MyISAM) seguem um paradigma diferente para integridade de dados, chamado de “operações atômicas”. Em termos transacionais, as tabelas MyISAM sempre operam efetivamente no modo autocommit = 1. As operações atômicas geralmente oferecem integridade comparável com desempenho superior.

Como o MySQL Server oferece suporte a ambos os paradigmas, você pode decidir se seus aplicativos são mais bem atendidos pela velocidade das operações atômicas ou pelo uso de recursos transacionais. Essa escolha pode ser feita por mesa.

dmmd
fonte
Isso é verdade para DML e nem sempre para DDL.
Yevgeniy Afanasyev
4

Se ocorrer alguma exceção, a transação será revertida automaticamente.

Formato de transação do 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
fonte