Section: Initializing a form
« Creating a jforms file | ^ jForms: automatic forms | Displaying a form in a template » |
− Table of content
jForms stores all form data in a cache. In your controller, you'll access these
data through a form object, instantiation of a class inheriting from
jFormsBase
. That class is automatically generated by jForms by parsing
your XML file. As for DAO, the class is stored in a cache to optimize performance.
In your controller, you'll create, modify and destroy a form object with
jForms
static methods.
Forms and actions ¶
As described in Classic forms, you are advised to create a set of actions to manage a form completely (name of actions below are not mandatory of course, you could use which ever you want):
- "prepare" action. its role is to create a form object with jForms, pre-fill the form if needed and redirect to "show" action.
- "show" action. it will first get the form object and use it in a template to display its form and maybe user input errors of a preceding submit. Upon user submit, "save" action will be called.
- "save" action. the form object is retrieved filled with user inputs through a jForms method. Then, it could check data for errors. If so, it will redirect to "show" otherwise it will process data (save it to database for example) and redirect to "end".
- "end" action. it will destroy the form object (it will clean data stored in session), and could display a confirmation of user posting or anything else...
Create a form object ¶
Before using jForms, it is required to have an xml descriptive file and
that one of your action creates a form object associated. Such object is
returned form jForms::create()
static method.
As every other jForms static method, its first argument is a selector of an XML file and accepts a second optional argument which is an id for your form object.
Say you have an XML file contact.form.xml
in "main" module:
$form = jForms::create("main~contact");
$form is an object of the class generated after parsing of XML file. that
class inherits from jFormsBase
. With $form, you'll have an API to
manipulate form data.
In the example above, we don't use a second argument. As said it is optional. It is only needed if you want to display a form on some already existing data. For example, if you want to edit a product stored in database, you'll give its id to jForms.
Why? Browsers allow to open multiple tabs. As a result, a user could open the same product edit form more than once at the same time but on different products. To prevent saving data in the same session object (thus loosing inputs) for each opened form, you must set a unique id to the form object. This id can be anything (a string, a number) but generally you'll use a SQL record key.
If your XML is product.form.xml
located in shop module, here is an example
of a form object creation on an existing product:
$form = jForms::create("shop~product", $product_id);
Off course, if you want to edit a new product, no need of an id:
$form = jForms::create("shop~product"); // new product
Now, read again our previous example. We did not give an id to the contact form because we assume in this case this form is used to send a contact request, so it will always be an empty form.
Form initialization ¶
After form creation, you may need to pre-fill it.
Simple initialization ¶
Use setData()
method of your form object. Give it your control name as
first argument and its value as second.
$form->setData("firstname","Laurent");
You can of course, retrieve value from a dao, a business class or anything else.
Initialization from a dao ¶
If you retrieve values from a dao, there is a more efficient way to fill your
form. initFromDao()
method allows to fill multiple form fields. Indicate a
dao selector as first argument. The second argument should be the value of the
primary key of the record to retrieve, if the id of the form doesn't correspond
to this key (the key value can be an array of values if the primary key is on
several fields). So if you don't indicate a key, the id of the form will be took
as the key.
$form = jForms::create("shop~product", $product_id);
$form->initFromDao("shop~products");
Form will be filled with values of the record whose id is $product_id in
"shop~products" dao. You can also indicate a record object to initFromDao()
instead of a selector.
However, note that only controls whose name (or ref) are the same as dao properties will be pre-filled. Others will stay untouched.
You could use more than one dao to fill your fields. Your form fields could correspond to multiple tables in database for example.
Initialization of a multiple choice control ¶
<checkboxes>
and <listbox multiple="true">
controls could be filled
with more than one value. (Beware, here we talk about values to be pre-selected
and not the list if choices label).
You can still use setData()
by giving an array of values to pre-select:
$form->setData('categories', array(5, 8, 10));
But, commonly, those value lists reside in database and are retrieve from a join statement. ie. a table having as primary key two or more foreign keys. Each foreign key being a primary key of a different table. Imagine a table for products ('products'), a table for product categories ('categories'), and a product can belong to more than one category, you will construct a join table ('products_categories'), associating product keys and category keys (product_id and category_id).
If you want to fill your listbox or your checkboxes, you should read from
'products_categories' table. jForms provide a method on your form object to
achieve this initControlFromDao()
:
$form->initControlFromDao("categories", "shop~products_categories");
Of course, you should before define a "shop~products_categories" dao. jForms
expects as first dao key the one identifying a product and as second one the one
identifying a category. Even if it is not the case, you could give an array fo
key names as third arg to initControlFromDao()
:
$form->initControlFromDao("categories", "shop~products_categories", array('product_id','category_id'));
Note that this example is an edit product form. Here we assume that the form id
corresponds to a product id and as so initControlFromDao()
will use it as
search criteria to retrieve category id.
Defining choices for multiple choices controls ¶
In jforms xml file, we have documented several ways to populate choices
of a <menulist>
, a <listbox>
, a <radiobuttons>
or a
<checkboxes>
controls (static data, dao, class).
Those could still be unsufficient. In that situation, declare neither
<item>
tag nor dao*
or class
attribute in you XML file. In your
controller, define your choices as follow:
// retrieve your choices in an associative array,
$data = array(...);
// create an object that implement jIFormsDatasource2
$datasource = new jFormsStaticDatasource();
$datasource->data = $data
// retrieve the control and assign data
$form->getControl('control_name')->datasource = $datasource;
In $data, keys should be value of choices and values should be label of choices.
Note : you should do this in "show" action, ie. action that display your form.
Activate/Deactivate controls ¶
There are situations where you do not want to display some controls of your
form. Use deactivate()
and isActivated()
. Those two methods expect a
control name as argument. The latter return true if the control is activated. It
is useful for checking a submitted form. The first one activate or deactivate a
control. It accepts an optional parameter indicating whether to deactivate (if
true) or activate (if false).
$form->deactivate('control_name');
// similar to
$form->deactivate('control_name', true);
// and to re-activate it :
$form->deactivate('control_name', false);
// check a control state :
if ($form->isActivated('control_name')) ...
Get an already created form ¶
In a specific action, you have created your form object with
jForms::create()
, but following actions need to get the same form object.
Two jForms methods for this purpose: get()
and fill()
.
get()
retrieve simply your form object. As for create()
, give it a form selector:
$form = jForms::get("shop~product");
If you had given an id on creation, you'll have to give it also to get()
:
$product_id = $this->param('id'); // id has been transmitted through http
$form = jForms::get("shop~product", $product_id);
Note: get()
method returns null if the form does not exist. It probably means
that user has mistepped action where your form was created. You should then
redirect him to this action. Or directly create your form (this could prove
sufficient on simple forms).
$form = jForms::get("main~contact");
if ($form === null) {
$form = jForms::create("main~contact");
}
fill()
method also returns a form object (in fact it calls
jForms::get()
), but achieve another functionality: it fills form data with
all contents received through HTTP post or get request. Clear, it should be
called after user submit (see preceding section on form submit).
$product_id = $this->param('id');
$form = jForms::fill("shop~product", $product_id);
Note: identifier (id here) is still transmitted manually but following jelix versions should automate this.
Adding fields dynamically ¶
The deactivation is the preferred method, because it is simpler, and by reading the XML file, you know all used controls.
However, in some case you need to add several controls, for exemple repeated controls, depending of external values. For example, in a form dedicated to a news article, when you modify it, you would like to add an input for each label of photos attached to this article. So you would add a control for each label, depending of the number of photos.
In this kind of case, you have to instancy some new controls by hand, and add them to the form, before to manipulate it in each action:
- retrieve the form with
jForms::get()
orjForms::create()
(but notjForms::fill()
, see below) - instancy an object which inherits from
jFormsControl
. There is a class for each type of controls:jFormsControlInput
,jFormsControlListbox
,jFormsControlRadiobuttons
,jFormsControlHtmlEditor
, etc. See the full list in the reference documentation. - add this object to the form object, with the
addControl
method, or theaddControlBefore
method to add the control after a specific one. - you can then display the form, or fill it with data of the request, by using
initFromRequest()
.
You have to add these fields each time you create or retrieve the form. Indeed,
each time you retrieve the form with jForms::get()
or
jForms::fill()
, jForms always instancy the form object generated by jForms
from what it is described in the XML file. So the form object doesn't contain
your fields. That's why you have to add your fields before the display, but also
before the retrieval of submitted data, so jForms know which data to retrieve.
1 – adding a field during the initialization:
$form = jForms::get('myModule~myForm');
$ctrl= new jFormsControlinput('identifiant');
$ctrl->required=true;
$ctrl->datatype->addFacet('maxLength',255);
$ctrl->label='Identifiant';
$ctrl->size=100;
$form->addControl($ctrl);
2 – adding a field after the form submit :
$form = jForms::get('myModule~myForm');
$ctrl= new jFormsControlinput('identifiant');
$ctrl->required=true;
$ctrl->datatype->addFacet('maxLength',255);
$ctrl->label='Identifiant';
$ctrl->size=100;
$form->addControl($ctrl);
$form->initFromRequest();
You can put the code which add the field, into a private method of your controller, so you don't have to duplicate it in each action you use the form.
- *Warning** : you mustn't use the
fill()
method, because this method do aget()
followed immediately byinitFromRequest()
, so it doesn't give you the opportunity to add your field before the retrieval of data. If you call it, your field won't receive corresponding data.
3 - During the dynamic fill of a control
If the form contain a list depending on an other control, a http request is made to the jforms controller to fill this list.
During this action, the controller emit an event jformsPrepareToFillDynamicList
before to generate the list content and to return it to the browser. This
event allow to prepare the form object with all expected controls.
The event has these parameters:
form
: the form object.controlRef
: the id of the control containing the list that will be generated.