CMS

индекс
77,39

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

Известное дело — разработку любого веб-приложения можно поделить на этапы, а сами этапы — на типовые задачи. Одной из наиболее часто встречающихся типовых задач является работа с формами. Каждый раз, когда программисту приходится сталкиваться с ней, можно словить некоторое уныние, если надоевшая рутина не оформлена подобающим образом. Прежде, чем уйти под кат, покажу вам, как реализована работа с формами в cogear:
$this->form->set('add-comments')
->input('subject',array('validation' => 'required|max_length[80]'))
->editor('body',array('validation'=>'required|min_length[5]'))
->buttons('send');
if($result = $this->form->result()){
  if($this->form->save('comments',$result)){
    redirect('/node_url');
  }
}
$this->form->compile();


* This source code was highlighted with Source Code Highlighter.


Выше показана наиболее простая, но очень эффективная форма работы, простите за каламбур, с формами.

Один код в ответе за все


Нет нужды создавать отдельные методы контроллера для ловли данных — все функции на себя берет один кусочек кода.

Первым делом мы задаем id формы, который пригодится нам при использовании хуков или же при ином деле.
$this->form->set('add-comments')

* This source code was highlighted with Source Code Highlighter.

После этого цепочкой задаются элементы и их параметры, а также кнопки формы.
    // Добавляем поле типа "текст"
    ->input('subject', array('validation' => 'required|max_length[80]'))
    // Добавляем поле типа "редактор"
    ->editor('body',array('validation'=>'required|min_length[5]'))
    // Задаем кнопки
    ->buttons('send');

* This source code was highlighted with Source Code Highlighter.


Сразу же небольшое отступления для ответов на возможные вопросы:
1. При формировании вывода label к элементу берется на основании указанной, либо общей переменной перевода (общие хранятся в разделе edit).
Допустим, у нас есть языковой файл, в котором заданы названия и описания наших полей.
[my_form]
subject = "Заголовок комментария"
subject_description = "Укажите заголовок комментария. Не более 80 символов."
body = "Текст"
body_description = "Текст комментария не должен быть слишком коротким – от 5 символов и больше, пожалуйста."



// Функция-ярлык для задания текущего раздела классу i18n
d('my_form');
$this->form->set('add-comments')
->input('subject', array('validation' => 'required|max_length[80]'))
->editor('body',array('validation'=>'required|min_length[5]'))
->buttons('send');
if($result = $this->form->result()){
  if($this->form->save('comments',$result)){
    redirect('/node_url');
  }
}
$this->form->compile();


* This source code was highlighted with Source Code Highlighter.


Если перед выводом формы задать текущий раздел переводов, то на выходе мы получим форму следующего вида:


2. Обработка ошибок идет автоматически на базе указанных правил. Никаких дополнительных действий с вашей стороны не требуется.


Работа с ошибками


Вы отправляете форму, и если валидация не проходит, форма выводится по-новой с отображением ошибок.


Можно усовершенствовать форму, добавив валидацию на Javascript до ее отправки.
// Функция-ярлык для задания текущего раздела классу i18n
d('my_form');
$this->form->set('add-comments')
->input('subject', array('validation' => 'required|max_length[80]','js_validation'=>'required|length[5,80]'))
->editor('body',array('validation'=>'required|min_length[5]','js_validation'=>'required|length[5,-1]'))
->buttons('send');
if($result = $this->form->result()){
  if($this->form->save('comments',$result)){
    redirect('/node_url');
  }
}
$this->form->compile();

* This source code was highlighted with Source Code Highlighter.


Обновив страницу увидим, что теперь скрипты не дадут нам отправить форму до того, как она будет заполнена корректно.


Если вы уже задались вопросом, почему отличаются правила для пре- и пост-валидации, отвечаю:
— Для пре-валидации используется доработанный класс MooTools.Floor Form Check.
— Для пост-валидации используется доработанная библиотека CodeIgniter (который лежит в основе движка).

Напоследок приведу более развернутый пример — с упрощенным созданием/редактированием топиков.

class Index extends Controller{
  …
  /**
   * Создание и редактирование топиков.
   *
   * @param  int    $id    id топика
   * @return  void
   */
  function createdit($id = FALSE){
    // Функция-ярлык для задания текущего раздела классу i18n
    d('node_edit');
    // Определяем, существует ли топик
    if($id && $node = $this->db->get_where('nodes',array('id'=>$id))->row()){
      /*
       * Если топик существует задаем иной заголовок страницы
       * Строка перевода выглядит следующим образом
       * …
       * edit = "Редактирование топика '%s'"
       * …
       */
      title(t('edit',$node->name));  
    }
    else {
      // Можно указать раздел перевода и явным образом
      title(t('node_edit create'));  
    }
    // Задаем имя формы
    $this->form->set('node-createdit')
    // Добавляем поле типа "текст"
    ->input('subject', array('validation' => 'required|max_length[80]','js_validation'=>'required|length[-1,80]'))
    // Добавляем поле типа "редактор"
    ->editor('body',array('validation'=>'required|min_length[5]','js_validation'=>'required|length[5,-1]'))
    // Задаем кнопки
    // Если топик не сущесвует или мы не в режим редактирования, то будет отображена кнопка "Сохранить" вместо "Создать".
    ->buttons(empty($node) ? 'create' : 'save');
    
    // Если топик существует — заполняем форму его значениями
    if(!empty($node)){
      $this->form->set_values($node);  
    }
    // Ловим результат обработки формы
    if($result = $this->form->result()){
      // Если топик существует — обновляем его
      if(!empty($node) && $this->form->update('nodes',$result,array('id'=>$node->id)){
        redirect('/nodes/'.$node->id);
      }
      // Создаем новый топик
      elseif($this->form->save('comments',$result)){
        redirect('/'.$this->form->insert_id);
      }
    }
    // Выводим форму
    $this->form->compile();
  }
  …
}


* This source code was highlighted with Source Code Highlighter.


Разумеется, в данном топик показаны самые простые примеры работы с формами в cogear — для простоты восприятия и наглядности.
Наша реализация работы с формами хорошо себя зарекомендовала за последний год — она позволяет сократить время на разработку типовых задача ввода и обработки информации до минимума. Да, в кому-то она может показаться не идеальной, но

Надеюсь, вам понравился краткий экскурс в собственные задумки.

Если есть вопрос — задавайте, если хотите продолжения — будет и оно.
_________
Текст подготовлен в ХабраРедакторе

Как вы организуете работу с формами?
+5
21 февраля 2010, 19:19
13

комментарии (23)

+6
tenshi #
и сдохни пытаясь это дело причесать под дизайн…
–1
CuamckuyKot #
Отчего же? Правь шаблон формы и стили — причесывай, не хочу. Также можно задать кастомный шаблон, если не хотите вносить изменения глобально.
+1
tenshi #
а можно пример шаблона?
–3
CuamckuyKot #
Да, конечно.
Пример шаблона
+2
tenshi #
пиздец х))))
0
standov #
В общем ничего нового, причем имхо не самая удачная реализация — у меня (и во многих нормальных ФВ) — Template pattern = формы это контейнеры, контейнеры могут включать в себя элементы (в т.ч. и контейнеры), форма сама является элементом. Элемент имеет HTML представление. В итоге все что у вас + в отображении вы ничем не ограничены — от тупого представление формы echo form на прототипе но на этапе наведения блеска
0
CuamckuyKot #
Если не трудно — приведите пример, для наглядности.
0
standov #
чего именно? работы с формой?

контроллер:
$form = new my_form($request, $instance);

if ($request->isPost() && $form->validate())
{
   $instance->save();
   /// redirect
}
$this->renderTemplate('template', get_defined_vars());


шаблон (функциональный прототип) — юзаю PHPTAL но сути не меняет
${form}


через пару дней :)
${form/getOpen}
   ${form/login}
${form/getClose}


через неделю:
${form/getOpen}
   login: <input name="login" value="${form/login/getValue}" />
${form/getClose}


форма примерно:
class my_form extends form {
    protected init()
   {
       $this->addRequired($this->add(new field('login', 'Login')));
   }

   protected validateLogin($value)
   {
       if ($value === 'god') throw new validation_exception('Reserved login');
   }
}

0
CuamckuyKot #
Похоже на реализацию в ZendFramework.
Вполне достойная реализация.
0
standov #
да, в общем ничего нового, я так выше и написал, просто немного проще и плюшки
+2
Stepler #
статью надо дополнить работой с дизайном. иначе будет много вопросов.
НЛО прилетело и опубликовало эту надпись здесь
0
CuamckuyKot #
Надо было просто добить понимание хуков. Посмотрите на suder'a и его напор – он далеко продвинулся в разработке.
0
inetlover #
Я разобрался, и мне понравилась идея реализации, вот только с полями radio я не разобрался, как их делать, так как в движке пока они не реализованы и некуда посмотреть, что бы сделать по аналогии.
+1
WebApelsin #
В отношении работы с формами, мне понравился подход ASP.NET MVC: правила валидации прописываются в самой модели, а форма — это лишь форма — набор инпутов, для конвертации пользовательского ввода в объект модели (или задания отдельных свойств модели)… т.е. главной фишкой является дата-биндер, который собственно и занимается биндингом данных в объект. После этого вступает уже механизм валидации модели… Это очень удобно, т.к. правила валидации прописываются один раз, и именно там где им и место… и валидация не обязательно связана с конкретной формой. Что касается рендеринга формы, здесь уже вам карты в руки, хотите руками, хотите хелперы пишите… Реализацию такого подхода на ПХП пока не встречал, а у самого пока руки не доходят.
+1
larikov #
Правильно ли я понимаю, что для изменения порядка следования полей, их имен и пр. придется менять контроллер, а не шаблон? Т.е. это забота программиста, а не дизайнера?
Так же контроллер занимается валидацией входящих данных, значит если мы в нескольких местах работаем с профилем пользователя, то столько же раз придется задавать правила валидации?
0
CuamckuyKot #
Шаблон задается единожды. Работа дизайнера сводится к минимуму.
Валидацией занимается отдельный класс CodeIgniter.
Если вам на разных страницах потребуется вывести одну и ту же форму (вопрос — зачем?), тогда да, вам нужно будет выстроить форму по-новой, потратив пару минут.
Формы могут быть переопределены извне через хуки.
+1
larikov #
>Шаблон задается единожды. Работа дизайнера сводится к минимуму.
Скорее, работа дизайнера перекладывается на программиста.

>Если вам на разных страницах потребуется вывести одну и ту же форму (вопрос — зачем?)
Вот например, форма регистрации, и форма редактирования профиля. Это не одна и та же форма, но данные, по сути — одни и те же. И та, и другая заносят данные в таблицу users, так зачем мне писать два разных валидатора, если можно поручить валидацию данных модели. Пусть она занимается обработкой данных, это ее задача, а не задача контроллера.

>Формы могут быть переопределены извне через хуки.
И это для того, чтобы сменить порядок следования полей?
0
CuamckuyKot #
1. Программист просто определяет порядок и настройки полей.
2. Форма регистрации по логике отличается от редактирования профиля. Явного класса валидатора не присутствует, вы просто указываете желаемые настройки в опциях поля, а движок сам уже делает всю работу.
3. Это для того, чтобы из других компонентов движке вносить любые изменения в форму. С учетом того, что любые компоненты отключаются/подключаются в один клик в админке — очень полезная особенность.
0
Novikov #
Давно хотел спросить, почему mootools, а не jquery?
0
CuamckuyKot #
Вы уже спрашивали раньше :-)
Так сложилось, что с MooTools познакомился раньше и начал работу именно с нее.
jQuery мне тоже очень по-душе, и я скорее всего буду использовать ее в следующей мажорной версии движка. Менять коней на переправе смысла нет.
0
Novikov #
Точно! Я и забыл.

На мой взгляд, jQuery перспективнее с точки зрения жизни продукта, сообщества, развития и т.п.
0
CuamckuyKot #
Согласен, но MooTools тоже не последний фреймворк. Как было упомянуто, следующая версия будет дружить с jQuery.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.