This part of Forms lib allows user to create custom forms in runtime and store them into database. The library itself provides only the structure with fields to be displayed for the end-user, e.x. as a contact form.
\Vegas\Forms\FormFactory
- generates form object based on provided data, used in frontend controllers\Vegas\Forms\InputSettings
- groups available settings for specific input
Following inputs can be added out-of-the-box to the created form:
When creating a form, you are able to specify following settings per each of them:
To make FormBuilder available in the application, you have to create a service provider inside app/services
directory.
use Phalcon\DiInterface;
use Vegas\DI\ServiceProviderInterface;
/**
* Class FormFactoryServiceProvider
*/
class FormFactoryServiceProvider implements ServiceProviderInterface
{
const SERVICE_NAME = 'formFactory';
/**
* {@inheritdoc}
*/
public function register(DiInterface $di)
{
$di->set(self::SERVICE_NAME, function() {
return new \Vegas\Forms\FormFactory();
}, true);
}
/**
* {@inheritdoc}
*/
public function getDependencies()
{
return [];
}
}
The example below show how to put some logic - it covers submitting form data into some URL address. We assume i18n service is enabled and application uses Mongo DBMS.
Foo
├── config
| ├── config.php
| └── routes.php
├── controllers
| ├── backend
| | └── FooController.php
| └── frontend
| └── FooController.php
├── models
| └── Form.php
├── views
| ├── backend
| | └── foo
| | ├── _form.volt
| | ├── edit.volt
| | └── new.volt
| └── frontend
| └── foo
| └── show.volt
└── Module.php
namespace Foo\Forms;
use Phalcon\Forms\Element\Text;
use Vegas\Forms\InputSettings,
Vegas\Forms\Element\Cloneable;
use Phalcon\Validation\Validator\PresenceOf;
use Vegas\Validation\Validator\SizeOf,
Vegas\Validation\Validator\Email,
Vegas\Validation\Validator\Url;
class Form extends \Vegas\Forms\Form
{
/**
* Initializes backend form of pages
*/
public function initialize()
{
$name = (new Text('name'))
->setAttribute('placeholder', 'Name')
->setLabel($this->i18n->_('Form title'))
->addValidator(new PresenceOf);
$this->add($name);
$url = (new Text('url'))
->setAttribute('placeholder', 'URL')
->addValidator(new Url)
->setLabel($this->i18n->_('Target URL'));
$this->add($url);
$items = (new Cloneable('inputs'))
->setAssetsManager($this->assets)
->setBaseElements((new InputSettings)->getElements())
->addValidator(new SizeOf(array('min' => 1)))
->setLabel($this->i18n->_('Inputs'));
$this->add($items);
}
}
This form is a recommended minimum:
name
field will be translated into slug
url
field will be the target URL where POST is going to be sent
inputs
fields is a list of inputs added to the created form
namespace Foo\Models;
use Vegas\Mvc\CollectionAbstract;
class Form extends CollectionAbstract
{
public function getSource()
{
return 'vegas_forms';
}
public function beforeCreate() {
parent::beforeCreate();
$this->generateSlug($this->name);
}
}
namespace Foo\Controllers\Frontend;
use Vegas\Mvc\Controller\ControllerAbstract,
Foo\Models\Form;
class FooController extends ControllerAbstract
{
public function showAction($slug)
{
$formModel = Form::findFirst(['conditions' => ['slug' => $slug]]);
$this->view->setVar('model', $formModel);
$this->view->setVar('form', $this->di->getShared('formFactory')->createForm($formModel->inputs));
}
}
namespace Foo\Controllers\Backend;
use Vegas\Mvc\Controller;
class FooController extends Controller\Crud
{
protected $formName = 'Foo\Forms\Form';
protected $modelName = 'Foo\Models\Form';
public function initialize()
{
parent::initialize();
$redirectToRootPath = function() {
$this->response->redirect(['for' => '/']);
};
$this->dispatcher->getEventsManager()->attach(Controller\Crud\Events::AFTER_CREATE, $redirectToRootPath);
$this->dispatcher->getEventsManager()->attach(Controller\Crud\Events::AFTER_UPDATE, $redirectToRootPath);
$this->dispatcher->getEventsManager()->attach(Controller\Crud\Events::AFTER_DELETE, $redirectToRootPath);
}
}
show.volt
with following code:
<h1>{{ model.name }}</h1>
<form action="{{ model.url }}" method="post" role="form">
{% for element in form %}
{% do element.setAttribute('class', element.getAttribute('class')~' form-control') %}
{% set hasErrors = form.hasMessagesFor(element.getName()) %}
<div class="form-group{% if hasErrors %} has-error{% endif %}">
<label for="{{ element.getName() }}">{{ element.getLabel() }}</label>
{% if hasErrors %}
<span class="help-block">
{% for error in form.getMessagesFor(element.getName()) %}
{{ error }}
{% endfor %}
</span>
{% endif %}
{{ element }}
</div>
{% endfor %}
<input type="submit" value="{{ i18n._('Submit') }}" class="btn btn-primary" />
</form>
new.volt
with following code (edit.volt
can be implemented nearly the same way):
<div class="row widget">
<div class="col-xs-12">
<div class="widget widget-default-spacer">
<div class="spacer spacer30"></div>
</div>
<div class="widget widget-page-header">
<h1>{{ i18n._('Add new form') }}</h1>
</div>
<div class="widget widget-default-spacer">
<div class="spacer spacer22"></div>
</div>
<div class="widget widget-admin-page">
<div class="well">
<div class="row widget">
<div class="col-md-12">
<div class="form-edit form-horizontal">
<form action="{{ url.get(['for':'admin/form', 'action':'create']) }}" method="post" role="form">
{{ partial('backend/foo/_form', ['form': form]) }}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
_form.volt
with following code:
{% for element in form %}
{% do element.setAttribute('class', element.getAttribute('class')~' form-control') %}
{% set hasErrors = form.hasMessagesFor(element.getName()) %}
<div class="form-group{% if hasErrors %} has-error{% endif %}">
{% if element != form.get('inputs') %}
<label for="{{ element.getName() }}" class="col-md-3" control-label">{{ element.getLabel() }}</label>
<div class="col-md-9">
{{ element }}
{% if hasErrors %}
<span class="help-block">
{% for error in form.getMessagesFor(element.getName()) %}
{{ error }}
{% endfor %}
</span>
{% endif %}
</div>
{% else %}
<div vegas-cloneable="1" class="form-group-add">
{% for row in element.getRows() %}
<fieldset>
<div class="form-group-add-item">
{% for item in row.getElements() %}
<div class="form-group">
<label class="col-md-2 control-label">{{ i18n._(item.getLabel()) }}</label>
<div class="col-md-10">{{ item }}</div>
</div>
{% endfor %}
</div>
</fieldset>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="form-group form-group-button">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-form-submit">{{ i18n._('Submit') }}</button>
<a href="{{ url.get(['for':'/']) }}" class="btn btn-form-cancel">{{ i18n._('Cancel') }}</a>
</div>
</div>
{% endfor %}
<input type="submit" value="{{ i18n._('Submit') }}" class="btn btn-primary" />
</form>
routes.php
:
return [
// Form display action
'form/show' => [
'route' => '/form/{slug}',
'paths' => [
'module' => 'Foo',
'controller' => 'Frontend\Foo',
'action' => 'show',
],
'params' => []
],
// Form builder manipulation actions
'admin/form' => [
'route' => '/admin/form/{action}',
'paths' => [
'module' => 'Foo',
'controller' => 'Backend\Foo',
],
'params' => []
]
];
Finally, build a form named Bar form using [APP_URL]/admin/form/new
address.
As a result, generated slug will be bar-form
, the form will be available on [APP_URL]/form/bar-form
.
These are small classes which provide data for select lists in created forms.
The only requirement for them is to implement \Vegas\Forms\DataProvider\DataProviderInterface
.
An example which provides country codes to a select list:
namespace Foo\Models;
class CountryCodeDataProvider implements \Vegas\Forms\DataProvider\DataProviderInterface
{
public function getName()
{
return 'Country';
}
public function getData()
{
return [
'AX' => 'Ålandseilanden',
'AF' => 'Afghanistan',
// ...
'SE' => 'Zweden',
'CH' => 'Zwitserland'
];
}
public function setOptions(array $options)
{
}
}
And add the reference to the class into app/modules/Foo/config/config.php
file:
//...
'formFactory' => [
'dataProviders' => [
// Keeps all classes used to provide data for multiple data input types
// Use fully qualified class names implementing DataProviderInterface as values
// The order here is how these options will be listed when selecting one.
'\Foo\Models\CountryCodeDataProvider',
]
]
//...
After these steps, a new option with label Country will appear in the builder form.