laravel не выполняет откат (удаление вставленных записей) при возникновении ошибки

#php #mysql #laravel #eloquent

#php #mysql #laravel #красноречивый

Вопрос:

У меня есть эта простая функция, и я хочу выполнить транзакцию laravel. Он вставляет первый (вторичный ресурс), в то время как второй (основной ресурс) содержит ошибку, и я хочу выполнить откат и удалить вторичный ресурс, как только произошла ошибка.

 try {
    DB::transaction(function () use ($request) {
        $Share = new SecondaryShares();
        $Share->secondary_name = $request->secondaryName;
        $Share->primary_id = $request->primaryId
        $Share->save();

        //error in primaryname==> correct is primary_name
        $Share = new PrimaryShares();
        $Share->primaryname = $request->primaryName;
        $Share->percentage = $request->percentage;
        $Share->visibility = $visibility;
        $Share->save();
    });
} catch (Exception $e) {
    dd('failed');
}
dd('worked');
  

Как я могу это исправить?

Заранее спасибо.

Комментарии:

1. Можете ли вы подробнее объяснить, что не работает с данным кодом?

2. вы уверены, что код в блоке catch выполнен? Также, что делает этот код? Некоторые инструкции неявно фиксируются в MySQL, и откат также может быть невозможен .

3. У меня есть две таблицы, первичная и вторичная. Я поместил преднамеренную ошибку в основной общий ресурс. Прежде всего, он вставит вторичный общий ресурс, а затем перейдет через код к основному. Как только ошибка возникнет, я хочу удалить вставленный дополнительный ресурс до

4. я советую вам предоставить table structures ( SHOW CREATE TABLE table ) для обеих таблиц и опубликовать SQL-код, который генерируется на основе этого кода..

Ответ №1:

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

Вариант 1: Закрытие транзакции

Вы можете поместить свой код в закрытие транзакции. Как только генерируется Exception (фактически Throwable ) или любой тип исключения, наследуемый от этих типов, транзакция будет откатана, и исключение будет повторно сгенерировано обработчиком закрытия:

 DB::transaction(function () {
    $model1 = MyModel::create();

    // model2 will not be created if model1 couldn't be created
    $model2 = MyModel::create();
});
  

Вы также можете завершить закрытие транзакции с помощью try-catch, чтобы перехватить любое исключение транзакции:

 try {
    DB::transaction(function () {
        $model1 = MyModel::create();

        // model2 will not be created if model1 couldn't be created
        $model2 = MyModel::create();
    });
} catch (Exception $e) {
    Log::error('Insert failed', ['exception' => $e]);
    return redirect()->back()->withInput();
}
return redirect()->route('form.success');
  

Вариант 2: Управление транзакциями

Или, в качестве альтернативы, вы можете управлять логикой транзакции самостоятельно. Но будьте осторожны, этот способ более опасен, потому что довольно легко забывается откат или фиксация:

 DB::beginTransaction();

$model1 = MyModel::create();
if ($model1->exists !== true) {
    DB::rollBack();
    Log::error('Insert 1 failed');
    return redirect()->back()->withInput();
}

$model2 = MyModel::create();
if ($model2->exists !== true) {
    DB::rollBack();
    Log::error('Insert 2 failed');
    return redirect()->back()->withInput();
}

DB::commit();
return redirect()->route('form.success');
  

На мой взгляд, использование варианта 2 дает намного больше кода и менее читабельно.


Кстати, Laravel использует пользовательские исключения базы данных. Он выдает a IlluminateDatabaseQueryException и no PDOException .

Комментарии:

1. «На мой взгляд, использование варианта 2 дает намного больше кода и менее читаемо». По моему мнению, вариант 2 может быть более понятным: вы открываете транзакцию, а затем модель не существует, вы собираетесь выполнить откат, если все в порядке, выполните фиксацию, первый вариант более подробный, потому что вы не знаете, что происходит под капотом, не раскрывая это в документации.. Возможно, следует вызвать функцию DB::transactionAutoCommitOnSuccessAndRollbackOnException , тогда вы знаете, что происходит, когда вы читаете имя функции

2. она выдается в catch, даже если исключение pdo. вариант 1: то же, что я использую вариант 2: не удаляет model1, если model2 выдает ошибку

3. вы уверены, что используете InnoDB как механизм обработки таблиц? @ahmad, вот почему я попросил тебя создать структуры таблиц.

4. Это вызвано тем, что вы перехватили неправильное исключение. Но это не имеет значения, потому что закрытие в любом случае приведет к откату транзакции. По крайней мере, до тех пор, пока вы не вмешиваетесь вручную с DB::rollBack() .

5. @RaymondNijland Код и логика DB::transaction($closure) чрезвычайно прямолинейны. Да, Laravel абстрагирует большую часть реальной транзакционной логики, предоставляя это закрытие, но это не значит, что это плохо. Исходя из моего опыта, я могу сказать вам, что при работе с вариантом 2 у нас было больше ошибок, чем с вариантом 1. И в документации к варианту 1 четко указано, как это работает — нет необходимости в преувеличенном имени метода.

Ответ №2:

Моя база данных — InnoDB, но я выяснил, что таблицы — это MyISAM. решаемая путем добавления этой строки кода в миграцию: $table-> engine = «InnoDB»;