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.

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.


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

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

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.


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

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.


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. */
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.