O uso de Transactions no Laravel

Posted on: October 16, 2020 08:29 PM

Posted by: Renato

Views: 48

Hoje me deparei com mais uma coisa que o laravel possui, que eu ainda não tinha usado, pesquisando vim um video no youtube de um indiano que manja pra caralho, e vou tentar passa para voces. Imagine que você precise inserir ao mesmo tempo dados em duas tabelas distintas, e imagina se der algo errado no caminho, e uma das operações de alguma das tarefas falhar. Pois é este é um caso que pode ser usado.

Para resolver isso existe o DB Transaction do laravel, que permite fazer o commit ou dá um rollback de uma ou mais operações do banco de dados.

Um exemplo que temos uma tabela chamada users e outra chamada account. Quero que quando inserir um novo usuário automaticamente crie uma nova conta para mesmo na tabela account.

//Recupera os dados do formulário

$dataForm = $request->all();

DB::transaction(function () {

    $newUser = User::create($dataForm);

    Account::create([

        'user_id'     => $newUser->id,

        'number'     => uniqid(date('YmdHms')),

    ]);

});

Se algo de errado no momento de fazer as operações em alguma das tabelas Users ou Accounts automaticamente o laravel faz o rollback e desfaz deixando as informações no banco de dados no formato original.

Database Transactions Manual

Exemplo mais didatico abaixo:

//Inicia o Database Transaction

DB::beginTransaction();

//Recupera os dados do formulário

$dataForm = $request->all();

$newUser = User::create($dataForm);

$newAccount = Account::create([

    'user_id'    => $newUser->id,

    'number'     => uniqid(date('YmdHis')),

]);

if( $newUser && $newAccount ) {

    //Sucesso!

    DB::commit();

} else {

    //Fail, desfaz as alterações no banco de dados

    DB::rollBack();

}

Na documentação tem alguns exemplos:

Handling Deadlocks

The transaction method accepts an optional second argument which defines the number of times a transaction should be reattempted when a deadlock occurs. Once these attempts have been exhausted, an exception will be thrown:

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
}, 5);

Manually Using Transactions

If you would like to begin a transaction manually and have complete control over rollbacks and commits, you may use the beginTransaction method on the DB facade:

DB::beginTransaction();

You can rollback the transaction via the rollBack method:

DB::rollBack();

Lastly, you can commit a transaction via the commit method:

DB::commit();

https://laravel.com/docs/8.x/database#database-transactions

 

Mas sobre Database transaction Methods

A transação é usada para manter o estado enquanto opera as operações do banco de dados. Existem três métodos principais que você pode usar nas transações. O Laravel fornece o método de transação nas fachadas do banco de dados.

Uso da transaction do banco de dados

O aplicativo financeiro precisa ser mais escalonável em termos de suas operações. Não apenas finanças, mas também alguns aplicativos onde ocorre transação de dinheiro usam transactions de banco de dados. No entanto, a transação é crítica na maioria deles.

Por exemplo, considere um módulo de assinatura no aplicativo. Em primeiro lugar, o usuário é inscrito no aplicativo e algum pagamento é feito por meio de API de terceiros, como STRIPE. https://stripe.com/

Ao usar a transactions, você pode usar o bloco try-catch para um tratamento excepcional. Em outras palavras, obteremos a exceção. E reverter as alterações. Você pode usar métodos manuais para isso. Porque vamos usar um bloco try-catch.

Implementação

public function create(Request $request)
{
    $validator = Validator::make($request->all(), [
        'username' => 'required|unique:users|max:255',
        'email' => 'required|unique:users|max:255',
        'amount' => 'required',
    ]);

    if ($validator->fails()) {
        return redirect()->route('user.index')
        ->withErrors($validator)
        ->withInput();
    }

    DB::beginTransaction();

    try{
        // Step 1 : Create User
        $user = new User();
        $user->email = $request->email;
        $user->username = $request->username;
        $user->save();

        //Step 2 : Stripe Api Call

        //Step 3 : Amount Charged
        $user_amount = new UserSubscriptionAmount();
        $user_amount->user_id = $user->id;
        $user_amount->amount = $request->amount;
        $user_amount->save();

        DB::commit();

        return redirect()->route('user.index')->with('success','Thank You for your subscription');

    }catch(\Exception $e){
        DB::rollback();
        return redirect()->route('user.index')
        ->with('warning','Something Went Wrong!');
    }
}

https://gist.github.com/lucenarenato/7ff7b6b5a18bcff6194ce3ea360c4a31

Portanto, considere a situação em que o usuário é criado. Mas o pagamento parcial não foi bem-sucedido. Nesse caso, o valor está sendo armazenado. Mas o stripe não ganha dinheiro. Portanto, isso levará a uma inconsistência no banco de dados. Portanto, a transação funcionará aqui perfeitamente. Mas uma exceção será detectada pelo bloco try e catch. Além disso, todas as operações do banco de dados são rollback. Desta forma, a inconsistência do banco de dados será gerenciada.

No entanto, você pode usar dois try-catch para salvar os dados. Em primeiro lugar, para criar o usuário e, em segundo lugar, para o valor da loja.

DB::beginTransaction();

try{
    // Step 1 : Create User
    $user = new User();
    $user->email = $request->email;
            $user->username = $request->username;
            $user->save();
}catch(\Exception $e){
    DB::rollback();
    return redirect()->route('user.index')
    ->with('warning','Something Went Wrong!');
}

try{
    //Step 2 : Stripe Api Call

    //Step 3 : Amount Charged
    $user_amount = new UserSubscriptionAmount();
    $user_amount->user_id = $user->id;
            $user_amount->amount = $request->amount;
            $user_amount->save();
}catch(\Exception $e){
    DB::rollback();
    return redirect()->route('user.index')
    ->with('warning','Something Went Wrong!');
}
DB::commit(); // All transaction will commit if statement reach on this

Portanto, você pode usar o código avançado acima no controlador. Isso irá segregar a inserção de dados. Portanto, ele retornará quando qualquer uma das exceções ocorrer.

Vantagem de transaction de banco de dados

A transaction nos fornece o fechamento. Dentro desse encerramento, se ocorrer uma exceção, ele fará rollback. Se as consultas forem executadas com sucesso, a transação será confirmada. No Laravel, as facedes do banco de dados fornecem a transaction. Ele fornecerá tanto para o construtor de consultas quanto para o ORM eloquente.

Acima de tudo, isso manterá o estado atômico. O armazenamento de dados ou obtém rollback. Quando você usa a transação, ela é armazenada ou rollback. Essa é a principal vantagem da transação. Portanto, muitos desenvolvedores colocam transações nas consultas de inserção e atualização.

Leia também,

DB::beginTransaction();

try {
    DB::insert(...);
    DB::insert(...);
    DB::insert(...);

    DB::commit();
    // all good
} catch (\Exception $e) {
    DB::rollback();
    // something went wrong
}

Se você usa PHP7, use Throwable em catch para capturar exceções de usuários e erros fatais.

For example:

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}


// MyController.php
public function store(Request $request) {
    return DB::transaction(function() use ($request) {
        $user = User::create([
            'username' => $request->post('username')
        ]);

        // Add some sort of "log" record for the sake of transaction:
        $log = Log::create([
            'message' => 'User Foobar created'
        ]);

        // Lets add some custom validation that will prohibit the transaction:
        if($user->id > 1) {
            throw AnyException('Please rollback this transaction');
        }

        return response()->json(['message' => 'User saved!']);
    });
};

 

Fontes:

https://laravel.com/docs/8.x/database#database-transactions

https://www.youtube.com/watch?v=iST3qsRZ8yc

https://blog.especializati.com.br/database-transactions-no-laravel/

Share
About Author

Renato

Developer

Add a Comment
Comments 0 Comments

No comments yet! Be the first to comment