Исключение Laravel QueryException в обход try-catch?

#php #exception #laravel #php-5.3 #soft-delete

#php #исключение #laravel #php-5.3 #мягкое удаление

Вопрос:

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

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

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

Теперь, когда он пытается принудительно удалить, он выдает исключение QueryException, но не попадает в блок catch. Я пытался изменить Exception на Exception, QueryException, IlluminateDatabase 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
}
  

Ответ Firebug:

 {"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();
}
  

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

1. Если вы используете Laravel 4.2, способ мягкого удаления изменился — смотрите здесь: laravel.com/docs/upgrade . Но если это не так, то, боюсь, я не знаю причины.

2. Пусть он выдает исключение, и он должен сообщить вам, какое исключение он выдает. Нужно просто скопировать и вставить это.

3. Нет. Это Laravel 4.1. Но спасибо за вашу помощь!

4. @user3158900 Извините, я не понял. Вы можете быть более конкретными?

5. Запустите его, и затем он должен вывести страницу ошибки в браузере, а страница ошибки должна содержать исключение, которое было сгенерировано. Вы должны просто иметь возможность скопировать и вставить это в свой код, где Exception есть. Например, на этой странице filp.github.io/whoops/demo он отображается RuntimeException так, чтобы вы могли поймать его catch(RuntimeException) в своем блоке try / catch. Можете ли вы также опубликовать свою forceDelete() функцию, возможно, есть основная проблема, которая вызывает эти исключения. Если запрос плохой, это все равно не будет иметь значения.

Ответ №1:

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

 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 .

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

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