Помогите разобраться с исключениями

Forever

Новичок
Сейчас изучаю Yii2 Framework, и заодно исключения.

Вторые в своем коде редко когда применял раньше, с непривычки не могу понять, как же лучше это , особенно в MVC. Подскажите , как правильно переписать вот этот код , чтобы ивлечь всю пользу и удобство из из исключений:

Есть форма регистрации TestForm.php, и в ней функция добавления пользователя в БД:
Код:
public function addUser()
    {
        if ($this->validate()){ //если все данные прошли фильтрацию, то
              
            $user = new User(); // приписываем их пользователю
            $user->setRegDate();
            $user->login = $this->login;
            $user->setPassword($this->password);
            $user->email = $this->email;
            $user->setAuthKey();
            if ($user->save()){ // и сохраняем
                return true;
            }
                      
        }
      
        return false;
      
    }
есть контроллер формы, controller.php и там такое действие:
Код:
 public function actionIndex()
    {
        $model = new TestForm();
        if ($model->load(Yii::$app->request->post()) && $model->addUser()){
            $model->answer = 'Ok';
        }
        else{
            $model->answer = 'Not ok';
        }
        return $this->render('index', ['model' => $model]);
    }
Хотелось бы посмотреть пример добавления сюда механизма исключений, при котором, если просто валиация не прошла, то answer = Not Ok, если прошла, то Ok, а если по какой то причине $user->save не произошел, то answer = Извините, возникли технические неполадки". Заранее спасибо.
 

AnrDaemon

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

А дальше,
1. подобный код удобнее писать в обратном порядке (if(failed) throw new Exception; дальше нормальная обработка) - уменьшается количество структурных блоков, легче читать код;
2. удобнее, когда валидация формы возвращает список ошибок, которые потом можно скопом отдать обратно клиенту;
3. исключения можно использовать на разных стадиях, зависит от фреймворка.

Хотелось бы посмотреть пример добавления сюда механизма исключений, при котором, если просто валиация не прошла, то answer
Выбери что-то одно. Либо исключения, либо код возврата.
 

Forever

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

А дальше,
1. подобный код удобнее писать в обратном порядке (if(failed) throw new Exception; дальше нормальная обработка) - уменьшается количество структурных блоков, легче читать код;
2. удобнее, когда валидация формы возвращает список ошибок, которые потом можно скопом отдать обратно клиенту;
3. исключения можно использовать на разных стадиях, зависит от фреймворка.


Выбери что-то одно. Либо исключения, либо код возврата.
Спасибо, разобрался более менее.
А как лучше всего контролировать вывод сообщений для пользователя?
Например, взять опять таки обычную форму.

Нужно выводить три варианта сообщений над ней:

1 )пользовательский ввод прошел проверку
2) не прошел
3) возникли технические проблемы при обработке запроса

Так вот, где именно лучше держать , а где менять переменную с текстом сообщения? Если дежрать в модели, а изменять по необходимости в контроллере, как я сделал выше - это нормальная практика?

Как ты бы написал такую логику?
 

AnrDaemon

Продвинутый новичок
Ты задаёшь вопрос исходя из предположения, что существует какой-то универсально лучший способ… его нет.

Если брать, скажем, условно-типичную форму регистрации, то даже тут в трёх полях масса вариантов и тонкостей.
Какие-то проверки можно с уверенностью производить прямо на клиенте, что-то придётся обрабатывать на сервере, явно либо через XHR.

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
исключение - это фатальная для продолжения корректной работы кода ситуация. Либо исключения, либо код возврата.
К сожалению, несколько фреймворков, в их числе yii и slim, используют исключения не по назначению.
Например, чтобы вернуть 404 или 301, надо бросить исключение определенного класса.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Например, взять опять таки обычную форму.
Как ты бы написал такую логику?
Дело в том, что "как бы мы написали" и "yii-way" - это религиозно разные подходы. В Yii есть инструменты, которые помогают сделать это быстро. Мы бы делали это в разы дольше, потому что мы делаем сложные сайты, которые простыми инструментами не сделать.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
перенес, вопросов по теории тут нет
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@AnrDaemon, ты вопрос-то читал?
механизма исключений, при котором, если просто валиация не прошла, то answer = Not Ok, если прошла, то Ok
какой еще код возврата из процедуры, это не С
 

whirlwind

TDD infected, paranoid
Исключения для управления это плохо. Есть такая штука exception safety. Возможность форсировать стек легко приводит к side effects -> exception safety -> object/data inconsistency
Это способ стрельнуть себе в ногу. Просто в силу специфики PHP lifetime ноги не критичная штука.

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
в общем, я не согласен с алгоритмом возврата 404 через throw new HTTPException,
потому что 404 и 301 - валидная и штатная ситуация для приложения, и проектировать эту страницу надо через штатный слой view,
но индусам нравится, а их несколько миллионов
 

WMix

герр M:)ller
Партнер клуба
в общем, я не согласен с алгоритмом возврата 404 через throw new HTTPException,
потому что 404 и 301 - валидная и штатная ситуация для приложения, и проектировать эту страницу надо через штатный слой view,
но индусам нравится, а их несколько миллионов
и как ты это делаешь?
PHP:
public function action(){
  if(!$found) $view->render('404');
}
 

AnrDaemon

Продвинутый новичок
потому что 404 и 301 - валидная и штатная ситуация для приложения
Для какого слоя приложения это штатная ситуация?
Для HTTP? Да.
Для бизнес-логики? Бизнес-логике до HTTP дела нет, она сказала "[бибип] отсюда, нет у меня такой [бибип]".
HTTP слой отловил этот бип и конвертировал его в 404.
 

whirlwind

TDD infected, paranoid
Ура, холивар! )

Для 500 это норм ситуация.

PS. Мне нравится Java-way. Там есть дефолтные хендлеры и при этом респонз - объект. Можно свой стайл выбрать. Но исключения никогда не дадут необработанный результат, что тоже верно.
 

Adelf

Administrator
Команда форума
PS. Мне нравится Java-way. Там есть дефолтные хендлеры и при этом респонз - объект. Можно свой стайл выбрать. Но исключения никогда не дадут необработанный результат, что тоже верно.
Причем тут ява? Во многих php фреймворках тоже есть и респонс обьект и дефолтные хендлеры. Но это холивар, да. уже долгий.
Проблема пути без исключений - необходимость постоянно вручную пробрасывать ошибку наверх.
Проблема пути с исключениями - необходимость контролировать как бы исключение не выскочило не в том контексте(какой-нибудь неудачный findOrFail приведет к 404 ошибке, хотя это не всегда должно происходить). да и множественные ошибки не всегда удобно обрабатывать.
 

whirlwind

TDD infected, paranoid
Проблема пути без исключений - необходимость постоянно вручную пробрасывать ошибку наверх.
Ну это как написать. Есть такая штука IoC. Представь другой кейс, например UI для standalone. Думаешь там декомпозиция и количество уровней иерархии классов поменьше чем в вебе? Нет. Но там ни у кого мозгов не хватает исключениями рисовать на экране. Просто люди делают как проще. И если это не вызывает особых проблем, то подход приживается.
 

Adelf

Administrator
Команда форума
Ну это как написать. Есть такая штука IoC.
ты предлагаешь напрямую просить обьект веб-респонса? или что?

Представь другой кейс, например UI для standalone. Думаешь там декомпозиция и количество уровней иерархии классов поменьше чем в вебе? Нет. Но там ни у кого мозгов не хватает исключениями рисовать на экране. Просто люди делают как проще. И если это не вызывает особых проблем, то подход приживается.
Ну конкретно экрано-рисовательная часть довольно плоская. там нет проблем вроде. А именно application слой вполне может ловить исключения об ошибке(сервер не отвечает!!) и отдавать нормальный ошибочный объект(не исключение) рисовательному слою.
 

whirlwind

TDD infected, paranoid
ты предлагаешь напрямую просить обьект веб-респонса? или что?
Ничего не надо просить. TDA

Если речь про валидацию, то я предлагаю объект validation result, который отвязан от локали и оперирует например сетом кодов и ключевых параметров. Этот кусок логики относится к конкретному контроллеру и не нуждается в размазывании по иерархии, его не нужно никуда передавать. После обращения к валидаторам можно это объект-результат стандартным конвертером преобразовать в что то нужное на стороне браузера.

А если речь про именно исключения, то здесь top-level exception хендлер вокруг точки входа во фронт контроллер/роутера что бы сделать правильный fail fast и это будет 500. Все эти 40X это кейсы строго в одном месте возникают около одной точки и хендлить их гдето внутри контроллеров это явно создавать проблему. Для всех остальных случаев для каких целей http response code устанавливать, для понтов? Если это какой то rest протокол поверх http, то тем более response объект и коды по своим правилам.

И в итоге видим, что в обычных случаях для ответа исключения не нужны, потому что проблему ты обозначил правильно. А в исключительных просто обрабатываются каким то top-level хендлером. Исключения остаются исключениями. Способом защиты системы от непредвиденных (неучтенных/нежелаемых к обработке) кейсов.

Вообще форсировать стек это всегда очень плохо. Это типа объектно-ориентированного goto. Ну представь, ты сделал рефакторинг и заюзал какой нибудь декоратор который стату считает после вызова декорируемого объекта, а потом в глубине иерархии вызовов кидаешь исключения. Исключение пролетает код декоратора мимо и какой нибудь счетчик в этом декораторе что нибудь не учитывает. Система сломалась.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Бизнес-логике до HTTP дела нет, она сказала "[бибип] отсюда, нет у меня такой [бибип]".
HTTP слой отловил этот бип и конвертировал его в 404.
1. Давай определимся, HTTP слой - это View или контроллер?
"конвертировал его в 404" - это view. У тебя View ловит исключения? Нелепость получается. Исключения должен ловить контроллер, и вызывать View - это его роль.

2. В Yii бизнес-логика бросает исключение HttpException - это смешение View с моделью. Именно это мне не нравится. Не должна модель оперировать HttpException, она должна бросать свое исключение.
 
Сверху