Laravel QueryException в обход try-catch?

Я использую Laravel 4 и Eloquent ORM. В моей системе, когда кто-то удаляет запись, он должен проверить, есть ли у нее связанные записи. Если это не так, то он может быть удален навсегда. Но если это так, просто выполните мягкое удаление.

Способ обработки этой ситуации: попробуйте принудительно удалить, и если он выдает исключение из-за ссылочной целостности, перехватите его и выполните softDelete. Я знаю, что это выглядит бесполезно, но это было сделано другим разработчиком, и я бы не хотел связываться с его кодом.

Что он сделал, так это удалил, а затем, если он выдал исключение, просто установил флаг, чтобы «деактивировать» запись. Это сработало хорошо. Однако, когда я вступил во владение, я внедрил softDeleting, чтобы сделать вещи менее бесполезными.

Теперь, когда он пытается принудительно удалить, он выдает исключение QueryException, но не попадает в блок catch. Я пытался изменить Exception на \Exception, QueryException, Illuminate\Database\QueryException, но безуспешно. Любые идеи?

Чтобы проиллюстрировать это лучше:

Это было так:

try
{
    $contact->delete();
}
catch(Exception $ex)
{
    $contact->status = 0;
    $contact->save();
    //this works
}

А теперь вот так:

protected $softDelete = true;

....

try
{
    $contact->forceDelete();
}
catch(Exception $ex)
{
    $contact->delete();
    //this doesn't work
}

Ответ пожарного жука:

{"error":{"type":"Illuminate\\Database\\QueryException","message":"SQLSTATE[23000]: Integrity constraint violation: 1451 
Cannot delete or update a parent row: a foreign key constraint fails (`tst_db\/contact_company`, CONSTRAINT `fk_contact_company_contacts_id` 
FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE) 
(SQL: delete from `contacts` where `id` = 28)","file":"\/Applications\/XAMPP\/xamppfiles\/htdocs\/application\/vendor\/laravel\/framework\/src\/Illuminate\/Database\/Connection.php","line":555}}

Это функция forceDelete() из Illuminate/Database/Eloquent/Builder.php:

    public function forceDelete()
{
    return $this->query->delete();
}

person Gustavo Silva    schedule 19.06.2014    source источник
comment
Если вы используете Laravel 4.2, способ мягкого удаления изменился — см. здесь: laravel.com/docs/upgrade . Но если нет, то, боюсь, я не знаю причины.   -  person Joel Hinz    schedule 19.06.2014
comment
Пусть он выдает исключение, и он должен сказать вам, какое исключение он выдает. Должна быть возможность просто скопировать и вставить это.   -  person user1669496    schedule 19.06.2014
comment
Неа. Это Ларавель 4.1. Но спасибо за вашу помощь!   -  person Gustavo Silva    schedule 19.06.2014
comment
@user3158900 user3158900 Извините, я не понял. Можете быть более конкретными?   -  person Gustavo Silva    schedule 19.06.2014
comment
Запустите его, и тогда он должен вывести страницу ошибки в браузере, а страница ошибки должна содержать выброшенное исключение. Вы должны просто скопировать и вставить это в свой код, где находится Exception. Например, на этой странице filp.github.io/whoops/demo отображается RuntimeException, поэтому вы поймал бы его с помощью catch(RuntimeException) в блоке try/catch. Можете ли вы также опубликовать свою функцию forceDelete(), возможно, есть основная проблема, которая вызывает эти исключения. Если запрос плохой, это все равно не будет иметь значения.   -  person user1669496    schedule 19.06.2014
comment
Он не выводится, потому что это запрос AJAX. Я обновил вопрос ответом Firebug.   -  person Gustavo Silva    schedule 19.06.2014
comment
Есть ошибки в app/storage/logs/laravel.log?   -  person Unnawut    schedule 19.06.2014
comment
@Unnawut да. То же, что и мой ответ Firebug, но с трассировкой стека. В основном исключение было вызвано Connection.php, который был вызван Builder.php, который был вызван Model.php, который был вызван моей инструкцией forceDelete().   -  person Gustavo Silva    schedule 19.06.2014


Ответы (1)


Ваш $contact->forceDelete(); вызовет метод в Illuminate\Database\Eloquent\Model, который имеет следующий код:

public function forceDelete()
{
    $softDelete = $this->softDelete;

    // We will temporarily disable false delete to allow us to perform the real
    // delete operation against the model. We will then restore the deleting
    // state to what this was prior to this given hard deleting operation.
    $this->softDelete = false;

    $this->delete();

    $this->softDelete = $softDelete;
}

Теперь произойдет то, что ваш код выдаст ошибку на $this->delete(); выше и выдаст исключение.

Итак, он достигает вашего catch, и вы снова звоните $contact->delete();. Таким образом, он получает еще одно QueryException, при этом $this->softDelete никогда не возвращается к true.

Что вам нужно сделать, это вернуть мягкое удаление и попробовать удалить его снова:

try
{
    $contact->forceDelete();
}
catch(Exception $ex)
{
    $contact->softDelete = true;
    $contact->delete();
}
person Unnawut    schedule 19.06.2014