====== Работа с формами ======
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|Вернуться к оглавлению]]