====== Работа с формами ====== InstantCMS включает механизм для быстрой разработки форм, заполняемых пользователем на сайте. Механизм работы с формами позволяет упростить и автоматизировать: * создание форм; * вывод форм в шаблонах; * получение данных из формы в массив переменных; * проверку полученных значений (валидацию); * сохранение заполненных полей при повторном показе форме. На этой странице рассмотрена только работа с формами из контроллера, то есть их подключение. Внутреннее устройство самих форм рассмотрено в соответствующем [[dev:forms|разделе]]. ===== Файлы и папки ===== Каждая форма принадлежит конкретному контроллеру и хранится в отдельном файле. Файлы форм находятся в папке **forms** внутри папки контроллера. Название файла определяется по принципу: **form_{название формы}.php**. Например, форма добавления нового товара в frontend-контроллере компонента **shop** может называться **item_add**. Однако, если мы собираемся с помощью одной и той же формы делать не одно действие, например, не только добавлять в базу новый товар, но и редактировать имеющиеся, имеет смысл назвать файл и класс, описывающий форму, более универсально: **/system/controllers/shop/forms/form_item.php** Подробное описание содержимого этого файла находится в соответствующем [[dev:forms|разделе]]. ===== Загрузка формы ===== Внутри контроллера форма может быть загружена при помощи метода **getForm($form_name[, $params])**, который принимает три аргумента: - Название формы - (необязательный) Массив параметров в виде пар //**'Название' => 'Значение'**// - (необязательный) путь, относительно директории контроллера. Например вы решили хранить некие формы в отдельной директории ''/system/controllers/shop/my_folder/forms/''. В этом случае в третий аргумент нужно передать ''my_folder/''. Например, чтобы подключить форму без параметров в контроллере компонента **shop**: $form = $this->getForm('item_add'); В результате, в переменную **$form** попадёт объект класса **cmsForm**, структура которого уже должна быть нами описана в файле **/system/controllers/shop/forms/form_item_add.php** Форма может менять свою структуру в зависимости от определённых условий. Например, одна и та же форма может использоваться и для создания записи в БД, и для редактирования. При этом, часть полей в форме может иметь смысл только при создании записи, а при редактировании этих полей быть не должно. Чтобы форма могла узнать контекст вызова, ей можно передать дополнительные параметры: $form = $this->getForm('item_add', array('do' => 'add')); или $form = $this->getForm('item_add', array('do' => 'edit')); Массив параметров может быть любым, мы сами определяем интерпретацию, разрабатывая шаблон для вывода формы. В файле, где мы опишем шаблон формы, эти параметры InstantCMS положит в одноимённые переменные (в данном случае, в переменную **$do**), благодаря чему, они могут быть использованы нашей программой для изменения структуры формы, с помощью конструкций, подобных нижеследующей: if ($do == 'edit') { ... Ниже этот приём будет использован в контексте разработки экшена. Чтобы дополнительные параметры были доступны в файле описания формы в виде переменных, они должны быть объявлены, как аргументы для метода init(): public function init($do=false) { ... ===== Вывод формы в шаблоне ===== Внутри шаблона контроллера форма может быть выведена при помощи метода **$this->renderForm($form[, $data, $attributes])** Например, экшен редактирования товара в контроллере компонента **shop** подготавливает параметры и вызывает форму, описанную в файле **form_item.php**: public function actionItemEdit($id){ // Загрузка товара из базы $item = $this->model->getItem($id); // Загрузка формы $form = $this->getForm('item_edit'); // Передача товара и формы в шаблон $this->cms_template->render('item_edit', array( 'do' => 'edit', 'item' => $item, 'form' => $form )); } Шаблон в файле **/templates/default/controllers/shop/form_item.tpl.php** (в данном примере, он общий и для добавления, и для изменения товара) выведет запрошенную форму с переданными параметрами/данными в окно контроллера (компонента):

setPageTitle($page_title); // Ту же информацию выводим // в заголовок окна/вкладки браузера // Всё готово, чтобы "нарисовать" форму с соответствующими данными // (для 'add' это будут значения по умолчанию, для 'edit' – данные из БД) $this->renderForm($form, $item, array( 'method' => 'post', // Метод передачи данных из браузера на сервер. 'action' => '' // Указание: какой программе передать данные пользователя из формы; // '' (пустая строка) означает: их надо вернуть вызвавшему экшену. ), $errors); // Здесь, после вывода формы, можно выводить ещё какую-то информацию, // если этого требует логика вашего веб-приложения... ?>
===== Получение данных из формы ===== До сих пор мы обращали внимание на способ выведения формы, предъявления её пользователю. Но это нужно не само по себе, а чтобы пользователь ввёл свои данные. Теперь надо разобраться: как забрать введённую пользователем информацию из формы, проверить её корректность и сохранить в базу данных? Обратите внимание на строку в предыдущем листинге: 'action' => '' // Указание: на какой URL передать данные пользователя из формы; // '' (пустая строка) означает: их надо вернуть вызвавшему экшену, т.е. текущему URL. Эта конструкция означает, что один и тот же фрагмент программы как выводит форму, так и забирает (анализирует) данные. То есть, этот экшен будет минимум дважды выполнен! Отличить – на каком «проходе» находится наша программа – позволяет специальная функция, определяющая – была ли в форме нажата кнопка submit? На этой кнопке в русскоязычных приложениях обычно написано «Сохранить» или «Отправить». Благодаря этой функции, на первом (выводящем форму) проходе действия по получению и проверке данных мы будем игнорировать, а на втором – выполнять. Чуть ниже мы познакомимся с этим методом. Для получения данных, переданных из формы, в InstantCMS 2 используется метод **parse**() экземпляра класса формы (отсюда выражение "парсить данные", т.е. получать и раскладывать по полочкам-переменным информацию): **$form->parse($request[, $is_submitted, $item])** Но, чтобы что-то //парсить//, надо убедиться, что данные уже отправлены из браузера клиента в наш экшен на сервере. Для извлечения (запроса) информации из разных объектов (в том числе, из формы), в InstantCMS 2 имеется класс, который так и называется "запрос" – **request**. Этот класс имеет метод **has**, позволяющий определить: был ли инициализирован объект, переданный в качестве аргумента? Вот, как проверяется в форме поле-кнопка 'submit': $is_submitted = $this->request->has('submit'); Теперь движок точно знает – выводит он сейчас форму или форма уже пообщалась с пользователем, и экшен вызвало нажатие на кнопку '**submit**'? А следовательно, можно принять решение о необходимости парсить данные, складывая их в структуру **$data**: // Если форма была отправлена if ($is_submitted) { // Получаем (request) и парсим (parse) данные из формы $data = $form->parse($this->request, $is_submitted); } ===== Валидация формы ===== Как показано в соответствующем [[dev:forms|разделе]], при создании структуры формы, мы можем каждому элементу управления придать некоторый набор правил: "обязательное", "максимальная длина строки", "не больше/не меньше определённого значения" и другие (полный список возможных правил см. в конце главы "Формы"). Правила эти нужны для того, чтобы можно было проверить корректность действий пользователя, заполнившего форму. Именно на данном этапе и можно провести такую проверку – **валидацию** (//от лат. validus ≈ здоровый, годный//) Для валидации данных, введённых пользователем, в соответствии с правилами, указанными при создании формы, используется метод экземпляра класса формы **validate**: **$form->validate($controller, $data[, $is_check_csrf])** // Кнопка 'Сохранить' нажата? $is_submitted = $this->request->has('submit'); // Если форма была отправлена if ($is_submitted) { // Получаем и парсим данные из формы $data = $form->parse($this->request, $is_submitted); // Проверяем данные (валидация) $errors = $form->validate($this, $data); // Если в форме найдены ошибки, // то $errors будет содержать массив вида 'поле' => 'текст ошибки' // Если ошибок нет, $errors будет содержать false } В качестве итога главы о программировании форм контроллерах InstantCMS 2, покажем, как может выглядеть экшен изменения сведений о товаре в контроллере компонента **shop**: public function actionItemEdit($id){ // Загружаем товар из базы $item = $this->model->getItem($id); // Загружаем структуру формы (см. главу "Формы") $form = $this->getForm('item_edit'); // Кнопка 'submit' нажата? $is_submitted = $this->request->has('submit'); if ($is_submitted) { // Следующие действия будут выполнены только если данный код был вызван, // в ответ на отправку формы из шаблона на сервер: // Получение и парсинг данных из формы $data = $form->parse($this->request, $is_submitted); // Валидизируем данные $errors = $form->validate($this, $data); // Если в форме найдены ошибки, // то $errors будет содержать массив вида 'поле' => 'текст ошибки' // Если ошибок нет, $errors будет содержать false // Если форма прошла валидацию if (!$errors) { // Сохраняем изменённые данные в базе данных $this->model->saveItem($id, $data); // Перенаправляем на просмотр изменённого товара $this->redirectToAction('view', array($id)); } } // Здесь мы окажемся либо на первом проходе, когда форма только // выводится пользователю первый раз, // либо, если форма не прошла валидацию, следовательно // не получилось перенаправления на страницу просмотра. // В обоих случаях, логично дать пользователю поработать // с формой (возможно, не первый раз) и отправить её на сервер. // Передача товара из формы в шаблон $this->cms_template->render('item_edit', array( 'do' => 'edit', 'item' => $item, 'form' => $form, 'errors' => $errors )); } ---- [[dev:controllers|Вернуться к оглавлению]]