Транзакции Laravel Eloquent ORM

96

Eloquent ORM довольно хорош, хотя мне интересно, есть ли простой способ настроить транзакции MySQL с использованием innoDB таким же образом, как и PDO, или мне придется расширить ORM, чтобы это стало возможным?

Wesside
источник

Ответы:

165

Ты можешь сделать это:

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

Все внутри Closure выполняется внутри транзакции. В случае возникновения исключения произойдет автоматический откат.

Лоуренс
источник
1
Внутри закрытия я могу вызывать запросы в классе? Будет работать?
Рафаэль Суфраз,
К сожалению, это не работает для меня, если я создаю экземпляры разных моделей, которые хранят записи в своих собственных соответствующих методах.
Volatil3
Если я поймаю исключение внутри своей транзакции (для создания сообщений об ошибках и т. Д.), Нужно ли мне повторно генерировать исключение, чтобы произошел откат?
Alexw
3
Хороший ответ, но меня зацепили несколько вещей: 1. Вам нужно добавить «use DB;» для этого, например, в верхней части файла модели 2. В отличие от JS, вы не получаете доступа к локальным переменным в родительской области, если вы явно не передадите их, поэтому вам нужно добавить конструкцию «использовать» таким образом ... DB :: транзакция (function () use ($ user) {... вставляет ссылку на $ user ...});
Polsonby
Discussed in more detail hereссылка мертва.
tomloprod 09
100

Если вам не нравятся анонимные функции:

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

Обновление : для laravel 4 pdoобъект больше не является общедоступным, поэтому:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}
Юрген Пауль
источник
15
Вы также можете использовать методы быстрого доступа DB::beginTransaction()& DB::commit()& DB::rollback(). Это было бы немного чище.
Флори
2
Обновите, чтобы использовать предложение @Flori. Это чище. Кроме того, перемещение нового ответа вверх сделает ваш ответ менее запутанным. Я использовал первый метод, прежде чем вернуться ко второму.
Frostymarvelous
Для более старой версии Laravel вам может потребоваться:DB::connection()->getPdo()->beginTransaction();
вместо
Я лично считаю, что DB::transactionобратный вызов with еще чище, но недостатком является то, что если вам нужно указать разные обработчики для разных исключений, вам придется вернуться, чтобы попробовать / поймать технику
OzzyTheGiant
33

Если вы хотите использовать Eloquent, вы также можете использовать это

Это всего лишь пример кода из моего проекта

        /* 
         * 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();
        });
Адитья Кресна Пермана
источник
question->idВыражение на обратный вызов транзакции возвращает ноль.
Христос Папулас
@ChristosPapoulas Вы имели в виду, что мы не можем получить идентификатор автоматического увеличения в транзакции?
hellojinjie
27

Если вы хотите избежать закрытия и использовать фасады, следуйте инструкциям, чтобы сохранить чистоту и порядок:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Если какой-либо оператор завершится ошибкой, фиксация никогда не будет выполнена, и транзакция не будет обработана.

Крис
источник
Если какие-либо инструкции терпят неудачу, последующие инструкции не будут выполняться. Вам по-прежнему нужно явно откатить транзакцию.
Джейсон
1
@Jason Я обновил ответ. Я был в двух мыслях о том, должен ли я, для большинства (всех?) Движков баз данных, когда соединение прерывается, любые транзакционные запросы, которые не зафиксированы, не будут зафиксированы. Тем не менее, я согласен с тем, что вы говорите, и, вероятно, лучше всего быть откровенным
Крис
19

Я уверен, что вы не ищете решение для закрытия, попробуйте это для более компактного решения

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}
имал хасаранга перера
источник
10

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

После прочтения ЭТОГО ответа stackoverflow я понял, что в моих таблицах базы данных используется MyISAM вместо InnoDB.

Чтобы транзакции работали на Laravel (или где-то еще, как кажется), необходимо, чтобы ваши таблицы были настроены на использование InnoDB.

Зачем?

Цитата из документов MySQL Transactions и Atomic Operations ( здесь ):

Сервер MySQL (версия 3.23-max и все версии 4.0 и выше) поддерживает транзакции с механизмами хранения транзакций InnoDB и BDB. InnoDB обеспечивает полное соответствие ACID. См. Главу 14, Механизмы хранения. Для получения информации об отличиях InnoDB от стандартного SQL в отношении обработки ошибок транзакций см. Раздел 14.2.11, «Обработка ошибок InnoDB».

Другие механизмы нетранзакционного хранения в MySQL Server (такие как MyISAM) следуют другой парадигме целостности данных, называемой «атомарными операциями». С точки зрения транзакций таблицы MyISAM всегда работают в режиме autocommit = 1. Атомарные операции часто предлагают сопоставимую целостность с более высокой производительностью.

Поскольку сервер MySQL поддерживает обе парадигмы, вы можете решить, лучше ли обслуживаются ваши приложения за счет скорости атомарных операций или использования транзакционных функций. Этот выбор может быть сделан для каждой таблицы.

dmmd
источник
Это верно для DML и не всегда верно для DDL.
Евгений Афанасьев
4

Если произойдет какое-либо исключение, транзакция откатится автоматически.

Формат транзакции 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. */
}
Срмилон
источник