Содержание
Работа с формами
InstantCMS включает механизм для быстрой разработки форм, заполняемых пользователем на сайте.
Механизм работы с формами позволяет упростить и автоматизировать:
- создание форм;
- вывод форм в шаблонах;
- получение данных из формы в массив переменных;
- проверку полученных значений (валидацию);
- сохранение заполненных полей при повторном показе форме.
На этой странице рассмотрена только работа с формами из контроллера, то есть их подключение. Внутреннее устройство самих форм рассмотрено в соответствующем разделе.
Файлы и папки
Каждая форма принадлежит конкретному контроллеру и хранится в отдельном файле. Файлы форм находятся в папке forms внутри папки контроллера. Название файла определяется по принципу: form_{название формы}.php.
Например, форма добавления нового товара в frontend-контроллере компонента shop может называться item_add. Однако, если мы собираемся с помощью одной и той же формы делать не одно действие, например, не только добавлять в базу новый товар, но и редактировать имеющиеся, имеет смысл назвать файл и класс, описывающий форму, более универсально:
/system/controllers/shop/forms/form_item.php
Подробное описание содержимого этого файла находится в соответствующем разделе.
Загрузка формы
Внутри контроллера форма может быть загружена при помощи метода 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 (в данном примере, он общий и для добавления, и для изменения товара) выведет запрошенную форму с переданными параметрами/данными в окно контроллера (компонента):
<?php // Выясняем: зачем мы выводим форму? if ($do == 'add') { $page_title = 'Добавление товара'; } if ($do == 'edit') { $page_title = 'Изменение товара'; } // Выводим теперь в окне контроллера заголовок, соответствующий задаче формы ?><h1><?php echo $page_title ?></h1> <?php $this->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); }
Валидация формы
Как показано в соответствующем разделе, при создании структуры формы, мы можем каждому элементу управления придать некоторый набор правил: «обязательное», «максимальная длина строки», «не больше/не меньше определённого значения» и другие (полный список возможных правил см. в конце главы «Формы»).
Правила эти нужны для того, чтобы можно было проверить корректность действий пользователя, заполнившего форму. Именно на данном этапе и можно провести такую проверку – валидацию (от лат. 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 )); }