Используем транзакции в Laravel

Ромчик
0

laravelДоброго времени суток. В данной статье мы поговорим о том, как использовать транзакции в Laravel. Транзакция — это атомарное действие над базой данных. Сложно? Ничуть. Дальше мы на примере рассмотрим, что такое транзакция в БД. И как их применять в Laravel.

Транзакции в базе данных.

При работе с базой данных мы должны поддерживать целостное состояние. И при изменении данных в базе данных мы должны перейти от одного целостного состояния к другому. Но возникают ситуации, когда при обновлении данных состояние целостности нарушается. Например, у нас есть два счета и мы хотим перевести средства с одного счета на другой. С точки зрения SQL:


    UPDATE accounts SET amount=amount-100 WHERE account="account_1"

    UPDATE accounts SET amount=amount+100 WHERE account="account_2"

Все просто. Но, если выполнится один запрос, а второй нет, то целостное состояние будет нарушено. Во избежание таких ситуаций в СУБД ввели понятие транзакции – атомарного воздействия на данные. Т.е. перевод базы данных из одного целостного состояния в другое. Другими словами, в транзакцию мы включаем несколько запросов, которые должны быть выполнены все, если же хоть один не будет выполнен, то и все запросы входящие в транзакцию будут не выполнены.

Подготовка проекта на Laravel.

У меня установлен Laravel 5.5.4

Версия Laravel

Настроено подключение к базе данных. В базе данных есть таблица accounts с данными.

База данных, с таблицей accounts и данными

Есть модель Account.


<?php

    namespace App;

    use Illuminate\Database\Eloquent\Model;

    class Account extends Model

    {

        protected $table = 'accounts';

        protected $fillable = ['account', 'amount'];

    }

Есть контроллер TestController с одним методом:


<?php

    namespace App\Http\Controllers;

    use Illuminate\Http\Request;

    use App\Account;

    class TestController extends Controller

    {

        public function index()

        {

        }

    }

И есть роут:


    Route::get('test', 'TestController@index');

Давайте реализуем перевод суммы с одного аккаунта на другой. Для этого изменим метод index в контроллере TestController:


    public function index()

    {

        Account::where('account','account_1')
            ->decrement('amount',100);

        Account::where('account','account_2')
            ->increment('amount',100);
    }

Проверяем:

Изменение счетов без транзакции, целостность состояния сохрянется

Мы с вами сделали перевод с одного аккаунта на другой не используя транзакций. И в нашем случае не произошло каких-либо сбоев. Поэтому и целостность состояния не нарушена.

Перевод средств с нарушением целостности состояния

Давайте вернем данные к исходным. На сумма на аккаунте 1 равна 1000, а на аккаунте 2 – 0.

Теперь сделаем перевод средств с одного аккаунта на другой с ошибкой. Для этого специально допустим ошибку в методе index контроллера TestController.


    public function index()

    {

        Account::where('account','account_1')
            ->decrement('amount',100);

        Account::where('account_','account_2')
            ->increment('amount',100);
    }

Проверяем:

Изменение счета с ошибкой, целостность состояния нарушена

Первый запрос прошел, а во втором возникла ошибка. И в итоге: средства с первого аккаунта ушли, а на второй не пришли. Целостность состояния нарушена. Чтобы такого не происходило и используются транзакции.

Перевод средств с использованием транзакции.

Давайте вернем данные к исходным: сумма на первом аккаунте равна 1000, а на втором 0.

Теперь поместим два наших запроса в транзакцию. Для этого в Laravel используется метод transaction фасада DB.

Изменим наш метод index контроллера TestController:


    public function index()

    {
        DB::transaction(function(){
            Account::where('account','account_1')
                ->decrement('amount',100);

            Account::where('account_','account_2')
                ->increment('amount',100);
        }, 5);

    }

Метод transaction принимает два параметра callback – обязательный и второй параметр не обязательный – это количество попыток (после использования, которых транзакция считается не успешной).

В методе index мы опять умышлено допустили ошибку, во втором запросе. Но сейчас оба запроса находятся в транзакции. Поэтому ни один не должен выполниться.

Проверяем:

Счета не обновились, сработала транзакция, целостность не нарушена

При выполнении второго запроса возникла ошибка. Из-за этого в целом транзакция не выполнена. Суммы на аккаунтах не изменились.

Давайте поправим метод index.


    public function index()

    {
        DB::transaction(function(){
            Account::where('account','account_1')
                ->decrement('amount',100);
            Account::where('account','account_2')
                ->increment('amount',100);
        }, 5);
    }

Проверяем:

Счета изменены, транзакция прошла успешно

Все запросы выполнены без ошибок, поэтому транзакция прошла успешно. Суммы на аккаунтах изменились.

В Laravel в фасаде DB есть еще несколько методов для управления транзакцией:

  • DB::beginTransaction() – для определения транзакции
  • DB::rollBack() – для отмены всех запросов после DB::beginTransaction()
  • DB::commit() – для выполнения всех запросов после DB::beginTransaction()

Заключение.

Мы  с вами разобрались для чего нужны транзакции в СУБД. И реализовали транзакции в Laravel. Рассмотрели два способа управления транзакциями: автоматический с помощью DB::transaction и ручное с помощью: DB::beginTransaction(),DB::rollBack(),DB::commit()

Понравилась статья? Поделись с друзьями.
  • Add to favorites
  • Добавить ВКонтакте заметку об этой странице
  • Twitter
  • Facebook
  • Мой Мир
  • LiveJournal
  • Одноклассники
  • Блог Я.ру
  • MySpace
  • FriendFeed
  • В закладки Google
  • Google Buzz
  • Яндекс.Закладки
  • Reddit
  • StumbleUpon
  • Technorati
  • del.icio.us
  • БобрДобр
  • LinkedIn
  • Memori.ru
  • Сто закладок
  • Blogger

©2012-2020 По всем вопросам обращайтесь через форму обратной связиПолитика конфиденциальности

Яндекс.Метрика