Работа с формами

Когда новички спрашивают что бы им написать посерьёзнее, обычно советуют гостевую книгу. Вариант хороший, в нём много разных путей реализации, можно повесить авторизацию, работу с деревьями, но самое главное — формы. Самое удивительное, что я встречал огромное количество (количество пишется с одним «л», когда уже избавлюсь от этой ошибки) «профессионалов», которые так и не научились делать удобные формы. Совет заказчикам при оценке партфолио всегда проверяйте форму обратной связи или регистрации-авторизации.

Жизнь формы:

  1. Вывод пустой формы или заполненной
  2. Отсылка заполненной
  3. Проверка-валидация данных
  4. Если не прошли валидацию, то выводим форму с уже введёнными данными и показываем где ошибка, форма не должна разваливаться из-за текста («>tets)
  5. Если всё верно — сохраняем данные, при необходимости обрабатывая, дабы не было инъекций и прочей гадости при выводе данных
  6. Делаем редирект, дабы на f5 данные не посылались заново.

Приступим.

Набросаем класс формы. Он должен уметь:

  1. Работать в своём нэймспэйсе, пространстве имён, чтобы на одной странице можно было разместить несколько форм. Например блок логина и контактную форму. Для этого будем передавать в конструктор имя, а название полей будут в виде массива имя_формы[имя_поля]
  2. Заполнять поле значением по умолчанию или брать значения из переменной $_POST[имя_формы][имя_поля]. Для ручной установки значения используем хак и будем это значение совать в суперглобальную переменную $_POST. Это не совсем хорошо, но очень удобно. Получаем методы getValue и setValue.
  3. Проверка данных. Для этого будут методы setInvalid в который передаются имя поля и текст ошибки, isInvalid для проверки прошли ли проверку (с именем — для конкретного поля, без имени для всей формы).
  4. Ну и последние, но очень важные функции для проверки страница открыта просто по ссылке или же была отправлена форма — метод isPost. А для проверки какая форма была отправлена будет метод isSubmit, который проверяет наличие нашего нэмспэйса в $_POST.

Вот что у меня получилось

<?php
class Form {
    protected $name;
    protected $validate = array();
    public function __construct($value = 'form') {
        $this->name = $value;
    }
    public function getName() {
        return $this->name;
    }
    public function getValue($field, $default = '') {
        return isset($_POST[$this->getName()][$field])
            ? htmlspecialchars($_POST[$this->getName()][$field],  ENT_QUOTES, 'utf-8')
            : $default;
    }
    public function setValue($field, $value) {
        $_POST[$this->getName()][$field] = $value;
    }
    public function isInvalid($filed = null) {
        if ($filed) {
            return isset($this->validate[$filed])
                ? ' <span style="color: red;">' . $this->validate[$filed] . '</span>'
                : false;
        } else {
            return !empty($this->validate);
        }
    }
    public function setInvalid($field, $text = '') {
        $this->validate[$field] = $text;
    }
    public function isPost() {
        return $_SERVER['REQUEST_METHOD'] == 'POST';
    }
    public function isSubmit() {
        return isset($_POST[$this->getName()]);
    }
}
?>

После обновления wordpress до 2.9.1 что-то случилось, вставка кода испортилась, tinymce кучу дополнительного кода генерит, прям в лучшем стиле ms word.

Теперь необходимо сорудить непосредственно обработчик

header('Conten-type: text/html; charset=utf-8');
require_once './lib/form.php';
$form = new Form('form');
if ($form->isSubmit()) {
    if (!$form->getValue('text2')) $form->setInvalid('text2', 'Заполните поле');
    if (!$form->isInvalid()) {
        header('Location: thank.php');
    }
} else {
    $form->setValue('text', 'значение по умолчанию');
}
include 'form.tpl.php';

Сперва отправили заголовок, чтобы не было проблем с кодировкой, затем подключили класс. Внимание это делается с помощью require_once, _once говорит, что подключаем файл только один раз, а require гарантирует, что в случае ошибки код не продолжится выполняться и не вылезет ещё десяток ошибок, как при include. Далее создали объект формы с именем form, проверили была ли отправлена данная форма. Если нет, то устанавливаем значение по умолчанию и выводим шаблон формы (include, так как не влечёт за собой других ошибок).

Если же форма была отправлена, то делаем проверку на заполнение поля, выставляем текст ошибки. если же не было ошибок !$form->isInvalid(), то сохраняем или что там и редиректим на thank.php.

Теперь рассмотрим сам шаблон

<form method="post" action="" name="<?=$form->getName()?>">
<dl>
    <dt>Простое текстовое поле</dt>
    <dd><input type="text" name="<?=$form->getName()?>[text]" value="<?=$form->getValue('text')?>"></dd>

    <dt>Текстовое поле с проверкой</dt>
    <dd><input type="text" name="<?=$form->getName()?>[text2]" value="<?=$form->getValue('text2')?>"><?=$form->isInvalid('text2')?></dd>

    <dd><input type="submit" value="Сохранить"/></dd>
</dl>
</form>

У формы метод post, get-ом я никогда не пользуюсь, action пуст, он отправит нас на ту же страницу, что нам и нужно, т.к. за вывод и обработку отвечает один скрипт. Имя формы берём из объекта формы. Имя полей тоже по принципу имя_формы[имя_поля], а value опять же из объекта, при этом у нас используется экранирование. После поля выводим сообщение об ошибке, если оно есть <?=$form->isInvalid(‘text2’)?>. И всё, легко и просто, а главное — эффективно.

Архив с кодом можно скачать здесь

Работа с формами: 11 комментариев

    1. admin Автор записи

      сомневаюсь, придётся делать все те же действия, плюс вместо редиректа, когда нужно отобразить другую страницу, делать location.href. к тому же проблемы с загрузкой файлов и tinymce, это решаемо, но как раз дополнительный геморой. зато появится новая проблема в плане юзабилити — пользователь отправил форму, страница не перегружается и он нажимает какую-нить ссылку, опять же проблема при передаче файлов.
      кстати, зааджаксить форму можно в пару строк на jquery, как раз об этом и статью следующую напишу

      1. sartas

        Какие-то ужасы про аякс написал.

        Пользователь отправляет аяксом форму. Сервер обрабатывает запрос и отправляет тект комментария или ошибку. Этот комментарий вставляется в нужное место на странице. Ну и форму очистить. Какие проблемы?

        1. admin Автор записи

          вот видишь, плох не сам ajax, а не продуманность действий.
          давай по очереди:
          1. нужен какой-то индикатор.
          2. запрет на отправку формы, чтобы два раза не отправили.
          3. ручное обновление капчи.
          4. могли быть добавлены ещё комментарии.
          5. если есть пэджинг, нужно следить за ним.
          6. возможность работоспособности форм без js.

  1. sartas

    Я же описал общий принцип работы комментариев на аяксе. Список действий определяет каждый сам для себя.

    Если комментарии отправляются только аяксом, запрет на повторную отправку формы не нужен. Хватит и очистки формы, данные все равно после обновления не отправятся.

    Мне капчу вводить не нравится, поэтому делаю отправку форм только с js и без капчи.

    С реализацией остальных пунктов особых проблем не должно возникнуть.

      1. admin Автор записи

        гы, вот такой ajax, я отвечал через админку на ваш коммент, а он ушёл неизвестно куда. :( чёт меня напрягает этот билд вордпресса, ещё и огромная проблема при вставке кода в редактор, раньше нормально вставлялся с отступами.

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

        мысль в том, что аякс — это куча дополнительных действий, а не облегчении жизни. при том, что очень много разработчиков не могут сделать удобную форму бес js, это очень актуально.

  2. Morgan

    >> action пуст
    Не легче использовать $_SERVER[‘REQUEST_URI’]?
    С пустым action’ом ваш сайт не будет валидным.

    1. AmdY Автор записи

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *