{{TOC}} Сегодня мы более подробно остановимся на теме, которую я затронул в предыдущей статье - "((6))". Надеюсь, у вас появится пару светлых идей, которые вы сможете разработать и использовать самостоятельно. Если вы ещё не прочитали ((6 предыдущую статье)) я советую вам сделать это перед продолжением, хотя, думаю, вы справитесь и без неё. == "Как это нужно делать" == Вы часто слышите эту фразу, верно? Я - да. Бесконечное число людей обсуждали со мной свои сумасбродные идеи о том, как нужно решать определённую проблему //правильно//. Это всё замечательно и хорошо, но в конечном счёте как это реализовать - целиком ваша забота. И когда я думаю о том, "как это нужно делать", то на самом деле я думаю о лучшем решении //для меня самого// - так что помните об этом. К слову, какие существуют общепринятые механизмы проверки пользовательского ввода? Часто говорят, что проверку //не нужно// делать в ((док3:controllers контроллере)) (или в [[док3:routing маршруте]]), но нужно это делать в ((док3:models модели)). Мне близка эта идея, я в любом случае люблю поддерживать хорошее ((ВП:разделение ответственности==)). Работая над проверкой я предпочитаю использовать //исключения// и блоки **try..catch**. == Сервис проверок == Для поддержания чистоты мы создадим класс-сервис для наших проверок. Он будет ответственным за создание правил, сообщений ошибок и собственно проверку. Он будет очень простой: %% input = $input; } protected function validate() { $this->validator = Validator::make($this->input, $this->rules, $this->messages); if ($this->validator->invalid()) { throw new ValidateException($this->validator); } } public function __set($key, $value) { $this->data[$key] = $value; } public function __get($key) { if (!array_key_exists($key, $this->data)) { throw new Exception('Значение [' . $key . '] не найдено в массиве данных Services\\Validation.'); } return $this->data[$key]; } } %% Довольно просто. Это //абстрактный класс//, поэтому мы не можем создавать его экземпляры и должны //расширить// его ("extend"). Вы, наверное, заметили, что в нём используются сразу два класса //исключений// - **Exception** и **ValidateException**. Второй из них нам нужно создать: %% errors = ($container instanceof Validator) ? $container->errors : $container; parent::__construct(null); } public function get() { return $this->errors; } } %% Сохраним этот код в **application/libraries/exceptions.php** для того, чтобы позже можно было легко добавлять новые исключения. Из-за названия класса и его нестандартного расположения нам нужно зарегистрировать его в ((док3:loading автозагрузчике)) (файл **application/start.php**): %% Autoloader::map(array( 'ValidateException' => path('app') . 'libraries/exceptions.php' )); %% С подготовкой обработки ошибок закончено - надеюсь, если что-то вам пока не понятно оно вскоре прояснится. В двух словах, мы хотим, чтобы когда вызывается метод проверки и он возбуждает исключение %%ValidateException%% мы можем его поймать и переадресовать клиента обратно к форме, но уже с сообщениями об ошибках. Конечная цель - та же, что и в ((6 предыдущей статье)), но в этот раз мы следуем принципам DRY, оптимально структурируя наш код. == Всё целиком == Раз мы всё настроили нам требуется что-то для проверки. Я буду использовать выдуманные значения для примера, но в вашем случае они будут поступать из формы или откуда-то из другого источника. Давайте предположим, что мы проверяем **форму комментария перед его публикацией**. Создадим сервис проверки комментария для поддержания высокой организации нашего кода: %% rules = array( 'name' => array('required'), 'email' => array('required', 'email'), 'comment' => array('required', 'max:200') ); $this->validate(); } } %% Если нам потребуется проверить другие типы данных мы можем создать метод для каждого из них - например, для проверки //редактирования комментария//. Хотя всё пока просто, вы можете пойти дальше и определить общие правила для проверок в //конструкторе нового подкласса//, а затем вызвать родительский //конструктор//. В общем, вы ограничены только своей фантазией. Теперь когда мы смотрим на ((док3:routing маршрут)) комментирования мы видим аккуратный блок **try..catch**: %% Route::post('posts/(:num)/comment', array('before' => 'csrf', function ($id) { try { $validation = new Services\Comments\Validation(Input::all()); $validation->publish(); } catch (ValidateException $errors) { return Redirect::to('posts/' . $id)->with_errors($errors->get()); } // Теперь мы можем добавить комментарий. Мы могли бы создать для этого новый сервис комментариев. Services\Comments\Creator::create(Input::all(), $id); }); %% Если проверка не прошла мы //ловим// исключение (**catch**) и //получаем// ошибки ("get"). Весьма просто. Вы можете пойти дальше и установить **try..catch** в методе %%Services\Comments\Creator::create()%% - и если он сработает, то просто повторно возбудите пойманное исключение: %% publish(); } catch (ValidateException $errors) { throw $errors; } // код добавления комментария. } } %% А теперь сам код //маршрута//: %% Route::post('posts/(:num)/comment', array('before' => 'csrf', function ($id) { try { Services\Comments\Creator::create(Input::all(), $id); // Комментарий был успешно добавлен. return Redirect::to('posts/' . $id); } catch (ValidateException $errors) { return Redirect::to('posts/' . $id)->with_errors($errors->get()); } }); %% Вот и всё. Теперь вы знакомы с несколькоими вариантами решения этой задачи. Я не утверждаю, что способ, продемонстрированный мной здесь - лучший или единственный; вполне возможно, что вы можете найти более подходящее под ваши условия решение. Это просто ещё один из методов проверки данных без лишних сложностей и с минимумом кода.