Forms

Until now we just fetched info from the database and displayed it. Most of the time we'll want to add data to the database, mostly using forms. Fork CMS supports different types of form fields, in the example beneath just a basic selection. How to use other types, check the Cheatsheet.

We'll be using the edit-form of an article. First we'll take a look at the execute method which is a little bit different than the detail action we discussed before.

Note that the structure of action files in back- and frontend is practically the same.

use Backend\Core\Engine\Model as BackendModel;
use Backend\Modules\MiniBlog\Engine\Model as BackendMiniBlogModel;

...

public function execute()
{
	$this->id = $this->getParameter('id', 'int');

	if ($this->id !== null && BackendMiniBlogModel::exists($this->id)) {
		parent::execute();
		$this->getData();

		$this->loadForm();
		$this->validateForm();

		$this->parse();
		$this->display();
	}
	else $this->redirect(BackendModel::createUrlForAction('Index') . '&error=non-existing');
}

Defining

In contrast to the frontend/detail-action example we added two methods. The first is the loadForm method to create the form. It add fields and supplies the values, most of the time, values we fetched in the getData method.

use Backend\Core\Engine\Form as BackendForm;
use Backend\Core\Engine\Meta as BackendMeta;
use Backend\Modules\Tags\Engine\Model as BackendTagsModel:

...

private function loadForm()
{
	$this->form = new BackendForm('edit');
	$this->form->addText('title', $this->record['title']);
	$this->form->addEditor('text', $this->record['text']);
	$this->form->addEditor('introduction', $this->record['introduction']);
	$this->form->addCheckbox('publish', $this->record['publish']);

	$this->form->addText('tags', BackendTagsModel::getTags($this->url->getModule(), $this->record['id']), null, 'inputText tagBox', 'inputTextError tagBox');

	$this->meta = new BackendMeta($this->form, $this->record['meta_id'], 'title', true);
}

First we create a Form object and give it a name. This is the name we use in templates.

Then we'll add the formfields. In our case a textfield, two textareas (editable with ckEditor) and a checkbox.

Form enhancements As you see we're adding a textfield for adding tags and you see a line about meta info. We'll be covering these topics in the following chapters.

Displaying

With Fork CMS you don't have to write all the form- and input tags yourself. Take a look at a part the code of the edit-template.

{% import "Layout/Templates/macros.html.twig" as macro %}
...
{% form edit %}
	{% form_field title %} {% form_field_error title %}

	<div id="pageUrl">
		<div class="oneLiner">
			<p><span><a href="{{ detailURL }}">{{ detailURL }}</a></span></p>
		</div>
	</div>
	<div class="tabs">
		<ul>
			<li><a href="#tabContent">{{ 'lbl.Content'|trans|ucfirst}}</a></li>
			<li><a href="#tabSEO">{{ 'lbl.SEO'|trans|ucfirst}}</a></li>
		</ul>

		<div id="tabContent">
			<table border="0" cellspacing="0" cellpadding="0" width="100%">
				<tr>
					<td id="leftColumn">

						<div class="box">
							<div class="heading">
								<div class="oneLiner">
									<h3>
									{{ 'lbl.Introduction'|trans|ucfirst }}
									{{ macro.required }}
									</h3>
								</div>
							</div>
							<div class="optionsRTE">
								{% form_field introduction %}
								{% form_field_error introduction %}
							</div>
...
{% endform %}

We start the form by using {% form nameOfTheForm %}. When writing this, it's replaced by the form-tag and a couple of hidden fields Fork CMS uses to determine if the form was submitted.

Typically when you want to add a form field to your template, you'll write three things.

  • A label → {{ 'lbl.Introduction'|trans|ucfirst }}
  • The formfield → {% form_field introduction %}
  • The error → {% form_field_error introduction %}

The label is not mandatory, but most of the times you want to tell the user what he/she is supposed to fill in.

Including the form field is as easy as writing a label, but, mind the prefix. A textfield and a textarea have a 'txt'-prefix, a checkbox 'chk', … For all prefixes, consult the Cheatsheet.

In the next chapter we'll cover how to validate a form and how to add errors to the form fields. When an error is added to a fields, automagically, an extra variable is supplied which you can call by using {$txtNameOfFormFieldError}.

An optional error As you now, every variable can be used as an option. So when an error is supplied you could use this as an option: <span {% if txtNrError %} class="showInRed"{% endif %}> {{ 'lbl.Nr'|trans|ucifirst }} {% form_field nr %} {% form_field_error nr %} This way, the label of the form is displayed in red.

When you're done writing all the fields, you close the form with {% endfor %}.

Validating

When the form is submitted you will process the submitted data, but you need to validate first. These two actions happen in the validateForm method.

private function validateForm()
{

First, you check if the form was submitted.

	if($this->form->isSubmitted())
	{

To make sure everything is in order, call the method cleanupFields. This method checks if there were fields added to the POST array that are not defined in the form. If so, these fields are deleted.

		$this->form->cleanupFields();

Then, you need to check if the supplied values are valid. That's something you need to program yourself, although you can use functions such as isEmail, isFilled, ... to make this task a bit less complicated.

	// validate fields
	$this->form->getField('title')->isFilled(BL::err('TitleIsRequired'));
	$this->form->getField('introduction')->isFilled(BL::err('FieldIsRequired'));
	$this->form->getField('text')->isFilled(BL::err('FieldIsRequired'));

	// validate meta
	$this->meta->validate();

The syntax you see here, BL::err('TitleIsRequired'), is used to fetch a translation. BL is shorthand for BackendLanguage, and err is shorthand for getError. (The syntax for doing this in a template would be {$errTitleIsRequired}).

The same thing you can do with FL, lbl, msg and act.

When you checked all the fields that are mandatory, you check if the form "is correct".

if($this->form->isCorrect())
{

isCorrect returns true if none of the functions you called on any of the form fields (isFilled, isEmail, …) added an error to the form.

Extra errors For some types of errors there are no build in functions in Fork CMS. Suppose you need to check if a value is a valid bank account number. You can check this yourself with a regular expression and when the number does not match it, you add an error yourself: $this->form->getField('account')->addError(BL::err('InvalidAccount'));

When the form is not filled in correctly, the page is opened like usual. But, Fork CMS detects the active POST-values and supplies them automagically to the form fields. Together with the fields, the errors are shown (if the placeholders are supplied).

If the form is filled in correct, an array is build with all the submitted data, and perhaps some extra data. F.e. the "edited" field with the current date and time.

use Backend\Core\engine\Authentication as BackendAuthentication;

...


		$item['id'] = $this->record['id'];

		$item['title'] = $this->form->getField('title')->getValue();
		$item['introduction'] = $this->form->getField('introduction')->getValue();
		$item['text'] = $this->form->getField('text')->getValue();
		$item['publish'] = $this->form->getField('publish')->getChecked();
		$item['user_id'] = BackendAuthentication::getUser()->getUserId();
		$item['meta_id'] = $this->meta->save();
		$item['edited'] = date('Y-m-d H:i:s');
		$item['language'] = BL::getWorkingLanguage();

We typically call an update function we defined in our (backend) model which will save the array as a record in the database. Mind that we added a field “id” to our array. The update function will use this field to update the right record.

		BackendMiniBlogModel::update($item);

		// save the tags
		BackendTagsModel::saveTags($item['id'],
		$this->form->getField('tags')->getValue(), $this->url->getModule());

		// add item to searchindex
		BackendSearchModel::saveIndex($this->getModule(), $item['id'],
		array( 'title' => $item['title'], 'introduction' => $item['introduction'],
			'text' => $item['text']));

		// trigger an action
		BackendModel::triggerEvent($this->getModule(), 'after_add');

Very important is the next line.

			$this->redirect(BackendModel::createUrlForAction('Index') .
 						'&report=added&var=' . urlencode($item['title']) .
 						'&highlight=row-' . $item['id']);
		}
	}
}

It makes sure that when our form is saved correctly, we're directed to (another) page before the headers were send. This way, this page is never accessed on the client side and so cannot be “refreshed” nor can it be viewed from the browsers history. This way you avoid that a user can keep posting items just by refreshing the page, because the POST-data would be refreshed together with the page.

HTML5 validation

HTML5 validates each field before your form is even submitted. The error messages are different for each browser and depend on the language of your browser. So, wouldn't it be nice if you can set custom error messages in the language of your website? Now you can!

You can set a data-error-[type] attribute on your form element with the error message as value of the data attribute. You can add multiple data-error- attributes, depending on the error you want to show.

Here's a basic example:

$formField->setAttribute(
    'data-error-required',
    'Oops! This field is required!'
);

And here's a more advanced example:

$formField->setAttribute(
    'data-error-required',
    Language::lbl('FieldIsRequired')
);

The supported types of errors are:

  • required (data-error-required)
  • email (data-error-email)
  • date (data-error-date)
  • number (data-error-number)
  • value (data-error-value)