достаточна и многогранна. Формы - один из элементов, который тесно связан с рендерингом на стороне клиента и сильно зависит от связи с клиентской стороной. Формы представляют собой ключевой момент при взаимодействии с пользователями, по этому формы незаменимы при создании веб-приложений.
Формы могут быть построены через объекты, или статическим образом. Статический способ подходит, если у вас используется одна форма на странице. Статическая форма внутри себя создаёт тот же самый объект
Статическая форма инициализируется автоматически при первом обращении к ней. Динамическую форму необходимо инициализировать вручную
$form = new c\form();
Статические формы представляют собой фабрику форм. Можно создавать больше одной формы и переключаться между ними через статические методы
По умолчанию - первая активна форма с ключом 'default'
<?php
// Создание новой формы
c\forms::createForm($form)
//переключение на созданную форму по умолчанию (получение текущей активной формы)
c\forms::activeForm($form=null);
//получение экземпляра объекта
c\forms::getForm($form=null)
После создания формы - можно управлять её свойствами
Настраивает форму на отправку данных через Ajax. Отправка данных идёт через отдельный javascript плагин jquery.form плагин добавляется в список автоматически. Реакция будет проходить в файле ajax.php (см раздел ajax)
c\forms::ajax($value=null,$form=null)
$form->ajax($value)
Устанавуливает или получает значение свойства ajax
GET POST
c\forms::method($method=null,$form=null);
$form->method($method=null);
В бутстрапе существует несколько разновидностей формы. По умолчанию - вся форма вертикальная. Для её переключения существуют методы
// inline
c\forms::formInline($form=null);
$form->formInline();
//horizontal
c\forms::formHorizontal($label_classes_array,$input_classes_array=12,$form=null)
$form->formHorizontal($label_classes_array,$input_classes_array=12);
$label_classes_array
- содержит массив отступов в сетке для каждого вида окна для поля label
array('sm'=>4,'md'=>3,'lg'=>2)
$input_classes_array - содержит массив общей длинны сетки для input для каждого вида окна. Либо число, меняющее количество для всех видов одновременно. Рекомендуется оставить 12 по умолчанию, если вы не меняете количество сеток в LESS SASS предкомпиляторах бутстрапа.
c\forms::classes($class=null,$form=null);
$form->classes($class=null);
c\forms::attributes($attributes=null,$form=null);
$form->attributes($attributes=null,$form=null);
c\forms::addClass($class,$form=null);
$form->addClass($class);
$form->classes
c\forms::addAttribute($key,$value,$form=null);
$form->addAttribute($key,$value);
$form->attributes[$key]=$value;
Основной ключевой элемент формы - наполнение форму полями. Вся форма представляет собой набор полей и строится по шаблону бутстрапа. Предусмотрено несколько типов полей, которые автоматически подгружают необходимые элементы, но все кастомизации идут на стороне Javascript и нужно обращать внимание на взаимодействие.
Все поля добавляются в форму всего через один метод
c\forms::addField($fieldKey,$fieldParams=null,$form=null);
$form->addField($fieldKey,$fieldParams=null);
поле $field представляет собой массив с набором полей. Ключи массива - используются в качестве имени поля по умолчанию. Они используются для получения данных и установки данных в форму.
Общая структура $field следующая
$field=array(
$key=>array( // field props
'name'=>array('prefix','name'),
'label'=>'label',
'label_html'=>false,
'label_full'=>'label full',
'fill'=>function($data){return $data['id'];},
'value'=>'value',
'type'=>'text' // submit,file,none,hidden,select,password,capcha,date,datetime,time,static,color
'render'=>'auto' // textarea,input,jqueryiu,check,radio,check-inline,radio-inline,select2,fancytree
'readonly'=>true // or false
'disabled'=>true // or false
'inputmask'=>'mask',
'placeholder'=>'placeholder text',
'subform'=>false,
'prefix'=>'$',
'postfix'=>'.00',
'default'=>'default field value',
'attributes'=>array('attribute_name'=>'attribute_value'),
'classes'=>array('field-class','another-class'),
'group_classes'=>array('field-group-class'),
'group_attributes'=>array('style'=>'display: block;'),
'filters'=>array('trim','lower'),
'helper'=>'helper text',
'helperHTML'=>false,
'range'=>false,
'datalist'=>array('set value'=>'shown string'),
'ico'=>'glyphicon glyphicon-plus',
'ajax'=>false,
'html'=>true,
'position'=>10,
'multiple'=>false,
'values'=>array(
1=>'possible',
2=>'values',
3=>'for',
4=>'select',
5=>'type',
6=>'field'
),
'validate'=>array(
array('type'=>'maxlength','value'=>100,'text'=>c\input::VALIDATE_AUTO_TEXT),
array('type'=>'required')
)
)
);
Свойств у полей присутствует множество. Все они в сочетании способны изобразить большую часть разных свойств форм. Поскольку формы тесно связаны с Javascript, фреймворк посратался сохранить грань и не лесть в клиентскую область, по этому часть данных придется сопрягать вручную на стороне Javascript. Остальная часть будет сама преобразовываться со значениями по умолчанию
За основу взяты формы в Bootstrap 3 , также нативно встроена поддержка Bootstrap 4 и MDB. У вас также есть возможность написать собственный рендер для отображения форм на любом css/js фреймворке
Ключ поля
- это его название по умолчанию. Название может быть перекрыто свойством name
. При этом name может содержать массив, в этом случае поле будет пребставлять из себя множество prefix[name]. Это очень полезное свойство для размещения данных из разных таблиц с одинаковыми названиями полей. Поля при получении данных будут разбросаны по массивам с префиксами.
label
- Ярлык поля. Описание назначения поля
label_full
- Полный ярлык поля (вместе с разделом). Если он указан - он используется для описания формы c\forms::getFormDescription()
label_html
- Выводить label целиком как он написан в оригинале, или применить к нему фильтр htmlspecialchars
helper
- текст под элементом, являющийся подсказкой
helperHTML
- boolean - выводить helper как html или использовать экранирование
datalist
- подсказки возможных быстрых значений, выводимых под элементом. При нажатии на значение - оно заносится в значение поля. Заполнение реализуется автоматически с использованием Jquery. Подходит, если в текстовом поле могут быть предустановленные значения
default
- Значение поля по умолчанию, если к форме не применялось свойство set_data().
ico
- выводимая над элементом иконка. Необходимо указывать полностью класс иконки.
value
- текущее значение поля. При описани формы - используется как значение по умолчанию
attributes
- Атрибуты поля. Указывается массив из ключей - значений, которые идут в атрибуты
readonly
и disabled
- значения, запрещающие пользователю проводить измения с полем. Помимо автоматического выставления атрибутов - поля не проводят валидацию
classes
- также декоративный элемент, содержащий атрибуты типа класс. Классы добавляются к существующему form-control классу
placeholder
- текст, который будет видео в поле, пока оно пустое. Автоматически также подгружается Javascript полифил.
prefix
, postfix
- Текст оформленный слева и справа от формы. Обычно указываются какие-то смежные величины, размерности, которые визуально сливаются со значением в поле, подсказывая пользователю конечный результат
inputmask
- Маска для ввода. Автоматически подгружается плагин inputmask и вводимые свойства вносятся в атрибуты
subform
- Используемая подформа. Элементом формы может быть другая форма, оформленная в виде объекта. Необходимо передать в качестве свойства - объект. Пример использования динамической формы на обнове подформы показан ниже. Ключ поля - используется как префикс для всех полей подформы.
filters
- Фильтры, которыми обрабатывается поле, как только в него были положены данные. Фильтры указываются через элементы массива, или через пробел. К значению применяются фильтры, указаные в c\input::filter
validate
- Правила валидации формы. Указывается массив правил, описаных в c\input::validate . С дополнительным параметром text - описываемым ошибку. В качестве текста ошибки также может быть передана константа c\input::VALIDATE_AUTO_TEXT
, которая подставит текст сама в зависимости от правила и перевода
position
- позиция элемента. Отвечает за расположение элемента среди других элементов при полном рендеринге c\forms::render()
range
- Является ли поле диапазоном значений минимум-максимум. Поле любого типа может быть разделено на два поля с минимальным и максимальным значением и ключами min, max. Может быть указан текстовый или числовой тип
type
- Тип поля. Один из основных параметров формы. Ниже будет указана специфика типов относительно типов полей. Основной элемент типа формы. Тип указывает на тип данных, содержащийся в поле. Возможны значения
render
- Тип отображения формы. Несколько типов формы могут содержать одинаковые значения и при этом совершенно по разному отображаться. К примеру text и textarea. select и radio, check. Возможны значения
values
- возможные значения полей для типа select
ajax
- для типа рендера select2 - для возможности указания внешнего источника для загрузки данных
multiple
- для типа select - возможность выбора нескольких значений
html
- указание value, целиком, как html для формы типа submit
Передается массив, у которого в качестве ключа указывается название поля, а в значении - его описание
c\forms::addFields($fields,$form=null);
$form->addFields($fields);
Для упрощенного добавления кнопки типа submit существует метод
c\forms::addSubmitField($params=array(),$fieldKey='submit',$form=null);
$form->addSubmitField($params=array(),$fieldKey='submit');
Добавляется элемент с типом submit и дополнительными параметрами, перечисленными в $params. Если $params указана строка - она автоматически устанавливается в свойство value.
$fieldKey - указывает на название кнопки. Для возможности встраивания в форму нескольких кнопок
Предупреждение: Вы можете использовать произвольные типы элементов, но при этом вы сами обеспечиваете сопряжение вводимых и выводимых значений, а также сопровождаете динамическое поведение элементов
// Пример подключения элемента типа slider
// у элемента должны быть атрибуты
// 'attributes'=>array('id'=>'slider'),
// 'range'=>true,
// 'render'=>'div'
var slider_min=$('#slider').parent().find('input').eq(0);
var slider_max=$('#slider').parent().find('input').eq(1);
$('#slider').slider({
range: true,
min: 0,
max: 100,
values: [slider.val(),slider.val()],
slide: function (event,ui){
slider_min.val(ui.values[0]);
slider_max.val(ui.values[1]);
}
});
// пример подключения bootstrap-datetimepicker
// имейте в виду, что данный плагин конфликтует с jquery-ui date picker
// 'classes'=>'month-picker',
$(function () {
$('.month-picker').datetimepicker({
locale: 'ru',
viewMode: 'years',
format: 'MM/YYYY'
});
});
Элементов формы на самом деле весьма много и менять однотипные моменты в большой форме (множестве форм) неудобно, изменяя каждый элемент. У пользователя есть возмонжность указать стратегии поведения элементов формы, задавая своё поведение и фактически расширяя стандартные элементы
Можно добавлять произвольные свойства и настраивать другие свойства относительно них
c\core::$data['form_attribute']['new-attribute']=function($item){
return $item;
};
соответственно, к любому элементу с атрибутом new-attribute
будет применена указаная пользовательская функция. Вы можете проставлять любые другие атрибуты, установив свой пользовательский тип и подгружать произвольные библиотеки
Для задания стратегии обработки по типам элемента и рендера - можно использовать конструкцию
c\core::$data['form_render']['type']=function($item,$args){
return string|array;
}
c\core::$data['form_render']['type']['render']=function($item,$args){
return string|array;
}
Любой тип поля и рендера можно использовать для произвольной отрисовки элемента
Если первый элемент массива type
представляет собой callback функцию - вызов происходит для всех элементов с указаным типом.
Если первый элемент представляет собой массив - второй элемент массива рассматривается, как поле с атрибутом render
Если функция возвращает строку - строка является конечной отрисовкой элемента и выводится на экран
Если функция возвращает массив - он подставляется в элемент и продолжает обработку по стандартному алгоримту рендера. Таким образом можно вешать дополнительные преобразования на любые пользовательские типы поля и рендера
В качестве аргумента $arg передается подготовленные участнки поля для вывода на экран, с экранированием
array( 'name','attributes','value','classes','multiple',placeholder')
Для рендера формы используется рендер бутстрапа. Рендер производится на стороне шиблона и выводит форму в html. Полный рендер возвращает всю форму целиком, по последовательности всех полей без лишних символов.
c\forms::render();
$form->render();
(string)$form;
Для более детальной прорисовки формы - рендер можно разделить на части по элементам
c\forms::renderBeginTag();
c\forms::renderField('FIELD');
c\forms::renderEndTag();
$form->renderBeginTag();
$form->renderField('FIELD');
$form->renderEndTag();
Необходимо указывать только существующие элементы, или экранировать их при помощи try ... catch ...
Элемент рисуется целиком через разметку form-group
Для более детальной пририсовки элемнента - используются функции рендера
$form->renderFieldFormGroupBegin($name,$props);
$form->renderFieldLabel($name,$props);
$form->renderFieldField($name,$props);
$form->renderFieldFormGroupEnd($name,$props);
Каждая функция рисует свою часть поля.
$name
содержит название поля
$props
содержит свойства, которые могли бы быть заменены. Например может использоваться для прорисовки разновидности элемента формы
Для вывода формы в качестве таблицы, например может использоваться для вывода разных отчетов, или писем - используется функция
$form->renderAsTable();
Если вы уже вывели часть полей формы и хотите вывести все остальные поля, а также предусмотреть расширение формы - можете воспользоваться функцией
$form->renderOtherFields();
c\forms::renderOtherFields();
Если вы хотите раскидать форму на колонки, step wizzard, или tabs и не хотите перечислять каждое поле в отдельности - вы можете указать диапазоны в порядке полей (свойство position) и ограничить вывод всех полей по позициям, либо указать им единый префикс
<?=$form->renderBeginTag()?>
<div id="column1">
Will show all fields with name=>['column1'][...] propertis
<?=$form->renderFieldsByName('column1')?>
</div>
<div id="column2">
Will show all fields with name=>['column2'][...] propertis
<?=$form->renderFieldsByName('column2')?>
</div>
<?=$form->renderEndTag()?>
<?=$form->renderBeginTag()?>
<div id="positionTo100">
Will show all fields with position 0-100
<?=$form->renderFieldsByPosition(0,100)?>
</div>
<div id="positionTo200">
Will show all fields with position 100-200
<?=$form->renderFieldsByPosition(100,200)?>
</div>
<?=$form->renderEndTag()?>
Достаточно сложная функциональность поддержка составных форм. Данный вид неразделим от интеграции с javascript. Все взаимодействия с javascript указаны ориентировочные, т.к. очень много зависит от динамических обрамлений формы и библиотек javascript.
Чтобы создать динамически повторяющуюся часть - необходимо заранее создать форму в виде объекта, после чего сделать форму частью элемента с типом 'subform'
$category=new c\form();
$category->addFields(
...
);
$mainform=new c\form();
$mainform->addField('category_prefix',
array(
'label'=>'Категории',
'type'=>'subform',
'subform'=>$category,
//'filter'=>'clear',
'validate'=>array(
array('type'=>'required','text'=>c\input::VALIDATE_AUTO_TEXT),
)
)
);
У нас структурно получилась основная форма $mainform, состоящая из одной или множества форм $category. В качестве валидации - можно указать type=required, что будет говорить о том, что хотя бы одна запись формы должна быть, а также можно указать фильтр 'clear', который удалит пустой экземпляр, если он не был заполнен. Все элементы формы будут добавлены с префиксом 'category_prefix'Невозможен в автоматическом режиме. По этому необходимо перечислять все поля формы. При отображении - необходимо заранее сформировать место, куда будут загружаться ранее созданные подформы, а также оставить место в шаблонах javascript, где будут храниться новые незаполненные динамические составляющие форм.
Отображение формы на уровне шаблона
<?=$mainform->renderBeginTag()?>
<div class="category">
<h3>Категории</h3>
<div class="category_list form-horizontal">
<?foreach ($mainform->getSubform('category') as $subform){?>
<div class="subform">
<a href="javascript:;" class="close" onclick="$(this).closest('.subform').remove();">×</a>
<div class="clearfix"></div>
<?=$subform?>
<hr>
</div>
<?}?>
</div>
<a href="javascript:;" class="btn btn-info" onclick="add_string('#category_template','.category_list');"><span class="glyphicon glyphicon-plus"></span></a>
</div>
<?=$mainform->renderEndTag()?>
<h2>Были отправлены следующие данные</h2>
<?=c\debug::dump($mainform->getData())?>
<div style="display: none;" class="templates">
<div id="category_template">
<div class="subform">
<a href="javascript:;" class="close" onclick="$(this).closest('.subform').remove();">×</a>
<div class="clearfix"></div>
<?=$mainform->getSubformTemplate('category','__')?>
<hr>
</div>
</div>
</div>
В данном примере идет завязка на jquery, bootstrap и glyphicon. Из новых функций - getSubForm - возвращает экземпляр подформы с заполненными значениями. Экземпляр может быть отрендерен автоматически <?=$subform?>.
Тоже самое происходит на стороне шаблона, только с отличием, что 2м аргументом идет префикс шаблона. Мы используем префикс __, чтобы разделять шаблонные динамические переменные от обычных переменных шаблона.
Остаётся только дать определение функции add_string
var ms=0;
function add_string(source,destination){
var html=$(source).clone(false).html();
html=html.replace(new RegExp('\\[__\\]\\[',"gi"),'['+(ms++)+'][');
$(html).appendTo($(destination));
}
$(function(){
ms=$('.subform').size()-$('.templates .subform').size();
})
По сути проходит клонирование шаблона в место, где располагаются подформы. А также происходит замена __ на переменную счетчика ms. Оно необходимо, чтобы в форме можно было использовать multi элементы, такие, как checkbox и select miltiple=true.
Счетчик расчитывается при загрузке страницы, как число динамичных элементов минут динамичные элементы в шаблонах. Можно использовать единый счетчик для всех возможных групп динамичных форм на странице.
Также в функции стоит предусмотреть уничтожение и создание динамичных кастомных элементов, если такие имеются в форме.
Для организации у в форме CSRF защиты - вы можете использовать стандартное поле типа hidden с передачей callback функции в качестве значения и валидации. Пример реализации:
<?php
c\forms::addField('csrf',array(
'type'=>'hidden',
'value'=>function(){
return sha1($_SESSION['user_id'].'salt'.date('my'));
},
'validate'=>array(
array('type'=>'callback','function'=>function($var){ return $var==sha1($_SESSION['user_id'].'salt'.date('my')); },'text'=>'form validation error'),
array('type'=>'required','text'=>'form validation error')
)
);
c\forms::isSubmit($form=null);
$form->isSubmit();
вернет ключ поля submit, которое было нажато
Чтобы установить данные внутрь формы - используется метод
c\forms::setData($data,$force=false,$form=null);
$form->setData($data,$namePriority=true,$force=false);
метод полностью заменяет все установленные ранее значения для формы переданными через $data значениями. Если $data не указана - используется $_GET или $_POST переменная в зависимости от метода формы.
При указании $namePriority в true - приоритет назначения данных определяется через свойство name каждого поля, а не ключа.
Свойство $force позволяет ингорировать поля, доступные только для чтения с атрибутами disabled или readonly. На случай, если нужно защитить данные поля от пользователя
Замечание: При установке галочек типа boolean - следует предварительно убрать текущие значения перед submit
c\forms::setData(['field_boolean'=>0]);
Чтобы добавить данные в форму к уже имеющимся ранее данным - используется метод
$form->addData($data=null,$namePriority=true,$force=false);
Для заливки данных через ранее зафиксированные методы формы fill
каждого поля - используется функция
c\forms::fillData($form=null);
$form->fillData();
Для получения ранее залитых данных - существует метод
c\forms::getData($form=null);
$form->getData();
Функция получает полный массив данных в соответствии со структурой формы. Преимущественно для обозначения ключей - используется свойтсво name.
Рекомендуется обращаться к данным пользователя именно через этот метод, чтобы отфильтровать лишние значения и автоматически привести данные к нужному виду
Прежде, чем оперировать с данными, полученными от пользователя - рекомендуется прогнать их через форму (ранее описанными методами)
Дополнительно можно использовать проверку формы на правильность заполнения. Для проверки используется свойство validate
каждого поля
Для проверки на корректность - используется метод
c\forms->isValid($data=array(),$checkData=false);
$form->isValid($data=array(),$checkData=false,$form=null);
//эквивалентно
!c\forms->isInvalid($data=array(),$checkData=false);
!$form->isInvalid($data=array(),$checkData=false,$form=null);
Функция вернет true или false в зависимости от результатов проверки, одновременно записав в c\error::add
текст ошибки при валидации
Обычно функция вызывается без аргументов, но для валидации внешних данных вне формы - можно их передать через $data
и указать $checkData=true
Подсчет осуществляется с учетом полей всех подчиненных форм, у которых установлено значение value. Чтобы узнать сколько полей из скольких заполнено - существует метод
$form->fillness(); //return array('total'=>всего полей,'fill'=>заполнено полей)
$form=c\forms::getForm($form=null);
$form->toForms($alias='default');
Создано при помощи сервиса Core CMS