MVC компонент

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

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

Формы могут быть построены через объекты, или статическим образом. Статический способ подходит, если у вас используется одна форма на странице. Статическая форма внутри себя создаёт тот же самый объект

Создание формы

Статическая форма инициализируется автоматически при первом обращении к ней. Динамическую форму необходимо инициализировать вручную

$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 - Тип поля. Один из основных параметров формы. Ниже будет указана специфика типов относительно типов полей. Основной элемент типа формы. Тип указывает на тип данных, содержащийся в поле. Возможны значения

  • Text (по умолчанию)
  • Submit
  • Hidden - поле скрыто
  • File - файл
  • None - поле как-бы не существует. Не участвует в прорисовке и валидации
  • Select - select (radio, check)
  • Password - пароль
  • capcha - капча
  • date - выбор даты
  • datetime - выбор даты и времени
  • time - выбор времени
  • static - статичный текст
  • color - выбор цвета
  • boolean (check, checkbox) - галочка

render - Тип отображения формы. Несколько типов формы могут содержать одинаковые значения и при этом совершенно по разному отображаться. К примеру text и textarea. select и radio, check. Возможны значения

  • auto - подставляется автоматически в зависимости от длинны значений
  • textarea
  • jqueryui - для типов date, datetime, time
  • check
  • radio
  • check-inline
  • radio-inline
  • fancytree
  • select2
  • tinymce
  • ckeditor
  • pick-a-color
  • div - пустой контейнер, который может использоваться, например для вывода jquery-ui слайдера

values - возможные значения полей для типа select

ajax - для типа рендера select2 - для возможности указания внешнего источника для загрузки данных

multiple - для типа select - возможность выбора нескольких значений

html - указание value, целиком, как html для формы типа submit

Добавление множества полей

Передается массив, у которого в качестве ключа указывается название поля, а в значении - его описание

c\forms::addFields($fields,$form=null);
$form->addFields($fields);

Добавление кнопки submit

Для упрощенного добавления кнопки типа 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();">&times;</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();">&times;</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 защита

Для организации у в форме 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