API documentationTable Of ContentsPrevious topicNext topicThis Page |
Forms¶Forms allows to handle user data. Forms contains such features:
Form example: <?php
namespace Core\Form\Admin\Setting;
use Core\Form\CoreForm;
/**
* Performance settings.
*
* @category PhalconEye
* @package Core\Form\Admin\Setting
* @author Ivan Vorontsov <ivan.vorontsov@phalconeye.com>
* @copyright 2013-2014 PhalconEye Team
* @license New BSD License
* @link http://phalconeye.com/
*/
class Performance extends CoreForm
{
/**
* Initialize form.
*
* @return void
*/
public function initialize()
{
$this->setTitle('Performance settings');
$this->addContentFieldSet()
->addText('prefix', 'Cache prefix', 'Example: "pe_"', 'pe_')
->addText(
'lifetime',
'Cache lifetime',
'This determines how long the system will keep cached data before
reloading it from the database server.
A shorter cache lifetime causes greater database server CPU usage,
however the data will be more current.',
86400
)
->addSelect(
'adapter',
'Cache adapter',
'Cache type. Where cache will be stored.',
[
0 => 'File',
1 => 'Memcached',
2 => 'APC',
3 => 'Mongo'
],
0
)
/**
* File options
*/
->addText('cacheDir', 'Files location', null, ROOT_PATH . '/app/var/cache/data/')
/**
* Memcached options.
*/
->addText('host', 'Memcached host', null, '127.0.0.1')
->addText('port', 'Memcached port', null, '11211')
->addCheckbox('persistent', 'Create a persistent connection to memcached?', null, 1, true, 0)
/**
* Mongo options.
*/
->addText(
'server',
'A MongoDB connection string',
null,
'mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]]'
)
->addText('db', 'Mongo database name', null, 'database')
->addText('collection', 'Mongo collection in the database', null, 'collection')
/**
* Other.
*/
->addCheckbox('clear_cache', 'Clear cache', 'All system cache will be cleaned.', 1, false, 0);
$this->addFooterFieldSet()->addButton('save');
$this->addFilter('lifetime', self::FILTER_INT);
$this->_setConditions();
}
/**
* Validates the form.
*
* @param array $data Data to validate.
* @param bool $skipEntityCreation Skip entity creation.
*
* @return boolean
*/
public function isValid($data = null, $skipEntityCreation = false)
{
if (!$data) {
$data = $this->getDI()->getRequest()->getPost();
}
if (isset($data['adapter']) && $data['adapter'] == '0') {
if (empty($data['cacheDir']) || !is_dir($data['cacheDir'])) {
$this->addError('Files location isn\'t correct!');
return false;
}
}
return parent::isValid($data, $skipEntityCreation);
}
/**
* Set form conditions.
*
* @return void
*/
protected function _setConditions()
{
$content = $this->getFieldSet(self::FIELDSET_CONTENT);
/**
* Files conditions.
*/
$content->setCondition('cacheDir', 'adapter', 0);
/**
* Memcached conditions.
*/
$content->setCondition('host', 'adapter', 1);
$content->setCondition('port', 'adapter', 1);
$content->setCondition('persistent', 'adapter', 1);
/**
* Mongo conditions.
*/
$content->setCondition('server', 'adapter', 3);
$content->setCondition('db', 'adapter', 3);
$content->setCondition('collection', 'adapter', 3);
}
}
Structure¶Root form class is abstract. So you can’t create it directly. Also it has abstract methods, that identifies form rendering feature. That’s why there is some simple form structure: AbstractForm
|
CoreForm EntityForm (trait)
|
|------- FileForm
|
|------- TextForm
Core form implements all necessary methods: <?php
namespace Core\Form;
use Engine\Form\AbstractForm;
/**
* Main core form.
*
* @category PhalconEye
* @package Core\Form
* @author Ivan Vorontsov <ivan.vorontsov@phalconeye.com>
* @copyright 2013-2014 PhalconEye Team
* @license New BSD License
* @link http://phalconeye.com/
*/
class CoreForm extends AbstractForm
{
const
/**
* Default layout path.
*/
LAYOUT_DEFAULT_PATH = 'partials/form/default';
use EntityForm;
/**
* Get layout view path.
*
* @return string
*/
public function getLayoutView()
{
return $this->_resolveView(self::LAYOUT_DEFAULT_PATH);
}
/**
* Get element view path.
*
* @return string
*/
public function getElementView()
{
return $this->getLayoutView() . '/element';
}
/**
* Get errors view path.
*
* @return string
*/
public function getErrorsView()
{
return $this->getLayoutView() . '/errors';
}
/**
* Get notices view path.
*
* @return string
*/
public function getNoticesView()
{
return $this->getLayoutView() . '/notices';
}
/**
* Get fieldset view path.
*
* @return string
*/
public function getFieldSetView()
{
return $this->getLayoutView() . '/fieldSet';
}
/**
* Resolve view.
*
* @param string $view View path.
* @param string $module Module name (capitalized).
*
* @return string
*/
protected function _resolveView($view, $module = 'Core')
{
return '../../' . $module . '/View/' . $view;
}
}
Text and file form extended from it and used for text rendering and file uploading features respectively. Entity trait used for forms that must be created according to some entity. Read more about each form type below. Elements¶Elements are objects and form/fieldset is a container for these objects. So you can add element to form by creating it and adding: <?php
// Create element.
$el = new Text("someName", [/*options*/], [/*attributes*/]);
// Add element with order 1001.
$this->add($el, 1001);
But this is a bit hard. So, there are exists some methods for element creation:
Default options of elements (not all allowed, and this is not a complete list, options can be added manually by element):
Non-default options:
List of all elements, their options and attributes:
Fieldsets¶Fieldset is a logical and/or visible separation. By default there are two fieldsets: content and footer. Content is for editable elements and footer is for buttons: <?php
class Create extends CoreForm
{
/**
* Initialize form.
*
* @return void
*/
public function initialize()
{
// Add elements to default content field set (field set key is 'form_content').
$this->addContentFieldSet()
->addText('name', 'Name', 'Name must be in lowercase and contains only letters.')
->addSelect('type', 'Package type', null, Manager::$allowedTypes)
->addText('title');
// Add buttons to footer (field set key is 'form_footer').
$this->addFooterFieldSet()
->addButton('create')
->addButtonLink('cancel', 'Cancel', ['for' => 'admin-packages']);
}
}
You can add your fieldsets or access them: <?php
// Get content field set.
$contentFieldSet = $this->getFieldSet(self::FIELDSET_CONTENT); // self::FIELDSET_CONTENT = 'form_content'
// Add new field set.
$fieldSet = new FieldSet('fieldSetName', 'Some legend, if needed', ['class' => 'css-class'], [/*... array of elements...*/]);
// Add elements.
// Elements adding methods are the same as for form class.
$fieldset->add<elementName>(...);
// Set flag for rendering feature, this will remove html div separation between elements, by default used for buttons at footer.
$fieldSet->combineElements(true);
// Adds css attribute to all elements inside fieldset with key: id="fieldSetName_elementName".
$fieldSet->enableNamedElements(true);
// Changes all elements css name attribute according to fieldset name: name="fieldSetName[elementName]".
$fieldSet->enableDataElements(true);
// Addd fieldset to form with order number 1001.
$this->addFieldSet($fieldSet, 1001);
Conditions¶Conditions allows to set relation between fields. For example we have 3 fields: select, text and text. Select and text must be visible always, but third text field must be visible only when select field has some specific value. Conditions allows you to setup such relation: <?php
// Parameters:
// 1) Field that will be checked on value change. Our "select".
// 2) Our "third text field" that will be shown/hidden.
// 3) Value that must be in select to satisfy this condition and show "third text field".
// 4) Comparison operator, you can find constants in Engine\Form\ConditionResolver. Allowed: ==, !=, >, <, >=, <=.
// This operator defines how value of fieldA must be compared to value that you entered in third parameter.
// 5) Summary operator. That operators also defined in Engine\Form\ConditionResolver. Allowed: 'and', 'or'.
// In case when "third text field" also related to "second text field" you can add new condition on that field,
// And in that case you will have two conditions, that's why you need to setup result operator - logical AND or OR.
// "third text field" will be shown/hidden state depends on result of conditions and their summary result.
$content->setCondition('fieldA', 'fieldB', 1, '==', 'and');
// Examples.
// Preconditions:
// select with values (1,2,3) - field1.
// text field - field2.
// text field - field3.
// Condition: field3 visible only when field1 has value '2' and field2 has value greater than '15'.
$content->setCondition('field1', 'field3', 2); // Comparison by default is '==' and result operator is 'and'.
$content->setCondition('field2', 'field3', 15, ConditionResolver::COND_CMP_GREATER);
// Condition: field3 visible when field1 has value '3' or field2 has lower or equivalent to '0'.
$content->setCondition('field1', 'field3', 3, ConditionResolver::COND_CMP_EQUAL, ConditionResolver::COND_OP_OR);
$content->setCondition('field2', 'field3', 0, ConditionResolver::COND_CMP_LESS_OR_EQUAL, ConditionResolver::COND_OP_OR);
// Condition: fieldSet 'footer' visible only when field1 has value '3'.
$this->setFieldSetCondition(self::FIELDSET_FOOTER, 'field1', 3);
This conditions allows to show/hide fields (all logic based on js, already implemented). Also it’s enables/disables validation for this fields and of course getValues method will return data without fields values if condition wasn’t successful. Form view¶AbstractForm class has some abstract methods:
This methods can be overridden, you can change one part of form view to your own. It means that you can simply change form style without problems to other forms. Layout view example: {{ form.openTag() }}
<div>
{% if form.getTitle() or form.getDescription() %}
<div class="form_header">
<h3>{{ form.getTitle() }}</h3>
<p>{{ form.getDescription() }}</p>
</div>
{% endif %}
{{ partial(form.getErrorsView(), ['form': form]) }}
{{ partial(form.getNoticesView(), ['form': form]) }}
<div class="form_elements">
{% for element in form.getAll() %}
{{ partial(form.getElementView(), ['element': element]) }}
{% endfor %}
</div>
<div class="clear"></div>
{% if form.useToken() %}
<input type="hidden" name="{{ security.getTokenKey() }}" value="{{ security.getToken() }}">
{% endif %}
</div>
{{ form.closeTag() }}
Entities support¶For example you have blog and you want to create form that will create blogs. You can associate form with entity and after validation you will have a new blog model. To associate form with entity you must add it per initialization. In most cases form for creation can be extended for form that will edit this blogs. So this must be respected: <?php
public function __construct(AbstractModel $entity = null)
{
parent::__construct();
if (!$entity) {
$entity = new Blog();
}
$this->addEntity($entity);
}
Done! To get your complete blog entity, just get it after validation. <?php
$this->view->form = $form = new CreateForm();
if (!$this->request->isPost() || !$form->isValid()) {
return;
}
$blog = $form->getEntity();
Note that blog already saved to database. If you don’t want to save it automatically, run validation with skip flag: <?php
$this->view->form = $form = new CreateForm();
if (!$this->request->isPost() || !$form->isValid(null, true)) {
return;
}
$blog = $form->getEntity(); // This entity isn't saved yet.
$blog->generateSlug();
$blog->save();
You can add multiple entities: <?php
public function __construct(AbstractModel $entity1 = null, AbstractModel $entity2 = null)
{
parent::__construct();
if (!$entity1) {
$entity1 = new Blog();
}
if (!$entity2) {
$entity2 = new Tag();
}
$this->addEntity($entity1, 'blog');
$this->addEntity($entity2, 'tag');
}
// In controller:
$blog = $this->getEntity('blog');
$tag = $this->getEntity('tag');
Validation¶Validation is divided. Validation can be defined for form, fieldset, entity. But all this validation is checked independently. If you like entity validation you can use it. For form validation internal validation system can be used. Example: <?php
$formOrFieldSet->getValidation()
->add('language', new StringLength(['min' => 2, 'max' => 2]))
->add('locale', new StringLength(['min' => 5, 'max' => 5]));
->add('email', new Email())
->add(
'controller',
new Regex(
[
'pattern' => '/$|(.*)Controller->(.*)Action/',
'message' => 'Wrong controller name. Example: NameController->someAction'
]
)
);
Filter¶Filter allows to filter entered values. There are some available filters (constants in AbstractForm class):
About filter system read in phalcon documentation. <?php
$form->addFilter('lifetime', AbstractForm::FILTER_INT);
Text Form¶This form is same as CoreForm, but it has changed views. In normal form all elements renders as control, in text form all element doesn’t renders, form takes only their values. CoreForm element view: <div class="form_element">
{% if instanceof(element, 'Engine\Form\Element\File') and element.getOption('isImage') and element.getValue() != '/' %}
<div class="form_element_file_image">
<img alt="" src="{{ element.getValue() }}"/>
</div>
{% endif %}
{{ element.render() }}
</div>
TextForm element view: <div class="form_element">
{% if instanceof(element, 'Engine\Form\Element\File') and element.getOption('isImage') %}
<div class="form_element_file_image">
<img alt="" src="{{ element.getValue() }}"/>
</div>
{% endif %}
{{ element.getValue() }}
</div>
File Form¶File form extended from CoreForm and contains additional checks for files validation, image transformation, files management, etc. FileForm is marked as ‘multipart/form-data’ and has additional methods. How to use it: <?php
$form = new FileForm();
if (!$this->request->isPost() || !$form->isValid()) {
return;
}
// Get all files from request.
$files = $form->getFiles();
// Get file of specific field.
$file = $form->getFiles('name');
Set file validation: <?php
$form->getValidation()->add('file', new MimeType(['type' => 'application/json']));
Set image transformations on upload (performed after validation, if valid): <?php
$form->setImageTransformation(
'icon',
[
'adapter' => 'GD',
'resize' => [32, 32]
]
);
‘adapter’ parameter is name of adapter that will be used (GD or Imagick). Other parameters are methods that will be called from adapter and value is parameters for this method ($gd->resize(32,32);). Entity Form (Trait)¶Entity trait was designed as light and simple way of form creation according to model. It applied to CoreForm as trait and can be accessible through different form types, for example text: <?php
$user = User::findFirst($id);
$this->view->form = $form = TextForm::factory($user, [], [['password']]);
$form
->setTitle('User details')
->addFooterFieldSet()
->addButtonLink('back', 'Back', ['for' => 'admin-users']);
EntityForm trait has one method “factory”: <?php
/**
* Create form according to entity specifications.
*
* @param AbstractModel[] $entities Models.
* @param array $fieldTypes Field types.
* @param array $excludeFields Exclude fields from form.
*
* @return AbstractForm
*/
public static function factory($entities, array $fieldTypes = [], array $excludeFields = []) {}
Field types parameter allows to change some fields html control (by default <input type=”text”/>). Exclude parameter allows to filter unnecessary fields. |