Definition of forms

As we said in the previous chapter, forms need to be defined and it doesn't matter if we are going to use the form only for data capture and validation or also for display. The basic definition is always common.

The principle of definition is actually quite trivial. A form is represented by an instance of the Jet\Form class. A form field is generally represented by an abstract class Jet\Form_Field, but it always has a specific form field type (and thus a specific purpose, properties, and overall form field behavior) that is represented by one of the classes. For an overview of field types and their classes, see here. This is the general principle, but let's go into some details.

Creating a Form Field Instance

There are two options for creating an array. Either traditional instance creation:

use Jet\Form;
use 
Jet\Form_Field_FileImage;

$img_field = new Form_Field_FileImage(
    
name'image'
    
label'Image:'
    
is_requiredtrue
);
$img_field->setMaximalFileSize2048 );
$img_field->setMaximalSizemaximal_width800maximal_height600 );

Or through a competent factory:

use Jet\Form;
use 
Jet\Form_Field;
use 
Jet\Form_Field_FileImage;
use 
Jet\Factory_Form;

/**
 * @var Form_Field_FileImage $img_field
 */
$img_field Factory_Form::getFieldInstance(
    
typeForm_Field::TYPE_FILE_IMAGE,
    
name'image',
    
label'Image',
    
is_requiredtrue
);
$img_field->setMaximalFileSize2048 );
$img_field->setMaximalSizemaximal_width800maximal_height600 );

In the sample application, you can see that within the application space, the classic approach of using the class name directly is used and, on the contrary, inside the Jet library (i.e. in the ~/library/Jet directory), factories are strictly used. We used to use factories within the application space as well to maintain consistency, but that proved to be impractical and kind of annoying.

The point of factories is to easily replace classes without affecting the core of the platform. So you need to be able to replace, for example, the Jet\Form_Field_Tel class with your own class, even if you frequently use automatic form generation by mapping to classes. That's where factories are simply essential.

However, in the application space you have everything under control and it is up to you what instance of what you create and possible replacement of a class with another one is not a problem in modern IDE - it is a work of a few moments. Hence the reason why internally Jet uses the factory to create forms, but in the application space (including micro-applications like the installer) it creates instances directly for practicality.

Form field name

Why bother with the name of the form field? There is one important specialty. For example, to write this text, I need the following fields in this site's content management system:

<input type="text" name="content[cs_CZ-initial][title]">
<
textarea name="content[cs_CZ-initial][annotation]"></textarea>
<
textarea name="content[cs_CZ-initial][description]"></textarea>
<
textarea name="content[cs_CZ-initial][key_words]"></textarea>
<
textarea name="content[cs_CZ-initial][text]"></textarea>

Now let's imagine for a moment that we are not in a Jet application, but pure PHP is used and I want to get the Czech text of this article from the request. Here's how I'll do it:

$text $_POST['content'][$locale.'-'.$version]['text'];

Simply put: We need to operate on forms and their data as an N-dimensional array.

However, as you probably already know, in the Jet application you can't even get to the superglobal fields $_GET and $_POST (in the normal configuration) and if it would be theoretically necessary to "reach" the POST directly (which is not necessary when working with forms and it is undesirable - but let's do it for the sake of illustration), then the data can be obtained as follows:

$text Http_Request::POST()->getString('/content/'.$locale.'-'.$version.'/text');

As you can see, the data in the array is accessed using a path. This is a universal approach that is used everywhere in Jet and is based on the base class Jet\Data_Array.

And it probably won't surprise anyone that the form field in the definition will be named in exactly the same way. Thus:

foreach( Application_Web::getBase()->getLocales() as $locale ) {
    
    
//*******************************************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
$text_field = new Form_Field_Textarea('/content/'.$locale.'-'.$version.'/text''Text:');
    
//*******************************************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    
$text_field->setDefaultValue$this->getContent($locale)->getText() );
    
    
$text_field->setFieldValueCatcher( function( $value ) use ($locale$version) {
        
$this->getContent($locale)->setText$value );
    });
    
    
//... ... ...
    
    
$form->addField$text_field );
}

Note: In the sample I'm a bit ahead of the topic and there is also a data capture (passing the value to an instance). We'll talk more about this in the extra chapter. But since I've shown a less than ideal approach here for demonstration, I had to show the correct one as well ;-)

Adding a field to a form

A form element needs to be in a form and a form needs form elements. That makes sense. But we'll show some interesting possibilities here as well.

Adding fields directly

The basic and probably most common option is to add fields to the form directly when creating its instance:

use Jet\Form;
use 
Jet\Form_Field_Input;
use 
Jet\Form_Field_Tel;

$name_field = new Form_Field_Input('name''Name:');
$tel_number_field = new Form_Field_Tel('tel_number''Tel. number:');

$form = new Formname:'contact_form'fields: [
    
$name_field,
    
$tel_number_field
]);

Adding fields to an existing form

For various reasons, we may need to add fields to an existing form. For example, it may be a form created by a parent class that we need to modify in a child, and so on. There are a number of possibilities.

use Jet\Form;
use 
Jet\Form_Field_Input;
use 
Jet\Form_Field_Tel;

//... ... ...

$name_field = new Form_Field_Input('name''Name:');
$tel_number_field = new Form_Field_Tel('tel_number''Tel. number:');

$form->addField$name_field );
$form->addField$tel_number_field );

It is even possible to do things like take a field from the first form, rename it, and place it in the second form:

$clone_field = clone $form_a->field('some_field');

$clone_field->setName('new_name');

$form_b->addField$clone_field );

Removing a form field

This is not a common situation, but in practice you will come across a situation where it is necessary/appropriate to remove a field from the form. This option also exists, of course:

$form->removeField'some_field' );

Name of form

As you have already noticed in the examples, not only the form field but also the form itself has a name:

use Jet\Form;

$form = new Formname:'some_form_name'fields: [
    
//... ... ...
]);

What's it good for? If you have more than one form on a page, it must be clear which form is currently submitted and therefore should be captured and processed. The form name is then used as the value of the hidden field. The name of this special hidden field can of course be changed either globally for the whole application, or for each form separately. The default name of this field is: _jet_form_sent_.

Warning! Although the form must have a name, the existence of a hidden form name field is not a requirement. After all, for a REST API like this, where forms are also used for capture and validation, this would be rather unfortunate. A form can be captured without this field, but we'll see that in another chapter.

Where to next?

Now you know the general principle of form definition. However, before you get into the interesting problem of capturing and validating forms as well as displaying them, I recommend you take a look at the class reference Jet\Form and also please check out the Jet\Form_Field class and form field types.

Previous chapter
Forms
Next chapter
Jet\Form