Form
-
class Cake\View\Helper\FormHelper(View $view, array $config = [])
The FormHelper does most of the heavy lifting in form creation. The FormHelper
focuses on creating forms quickly, in a way that will streamline validation,
re-population and layout. The FormHelper is also flexible - it will do almost
everything for you using conventions, or you can use specific methods to get
only what you need.
Starting a Form
-
Cake\View\Helper\FormHelper::create(mixed $context = null, array $options = [])
$context
- The context for which the form is being defined. Can be an ORM
entity, ORM resultset, Form
instance, array of metadata or null
(to make a
model-less form).
$options
- An array of options and/or HTML attributes.
The first method you’ll need to use in order to take advantage of the FormHelper
is create()
. This method outputs an opening form tag.
All parameters are optional. If create()
is called with no parameters
supplied, it assumes you are building a form that submits to the current
controller, via the current URL. The default method for form submission is POST.
If you were to call create()
inside the view for UsersController::add()
,
you would see something like the following output in the rendered view:
<form method="post" action="/users/add">
The $context
argument is used as the form’s ‘context’. There are several
built-in form contexts and you can add your own, which we’ll cover below, in
a following section. The built-in providers map to the following values of
$context
:
An Entity
instance or an iterator will map to
EntityContext;
this context class allows FormHelper to work with results from the
built-in ORM.
An array containing the 'schema'
key, will map to
ArrayContext
which allows you to create simple data structures to build forms against.
null
will map to
NullContext;
this context class
simply satisfies the interface FormHelper requires. This context is useful if
you want to build a short form that doesn’t require ORM persistence.
Once a form has been created with a context, all controls you create will use the
active context. In the case of an ORM backed form, FormHelper can access
associated data, validation errors and schema metadata. You can close the active
context using the end()
method, or by calling create()
again.
To create a form for an entity, do the following:
// If you are on /articles/add
// $article should be an empty Article entity.
echo $this->Form->create($article);
Output:
<form method="post" action="/articles/add">
This will POST the form data to the add()
action of ArticlesController.
However, you can also use the same logic to create an edit form. The FormHelper
uses the Entity
object to automatically detect whether to
create an add or edit form. If the provided entity is not ‘new’, the form
will be created as an edit form.
For example, if we browse to http://example.org/articles/edit/5, we could
do the following:
// src/Controller/ArticlesController.php:
public function edit($id = null)
{
if (empty($id)) {
throw new NotFoundException;
}
$article = $this->Articles->get($id);
// Save logic goes here
$this->set('article', $article);
}
// View/Articles/edit.php:
// Since $article->isNew() is false, we will get an edit form
<?= $this->Form->create($article) ?>
Output:
<form method="post" action="/articles/edit/5">
<input type="hidden" name="_method" value="PUT">
Note
Since this is an edit form, a hidden input
field is generated to
override the default HTTP method.
In some cases, the entity’s ID is automatically appended to the end of the form’s action
URL. If you would like to avoid an ID being added to the URL, you can pass a string to $options['url']
, such as '/my-account'
or \Cake\Routing\Router::url(['controller' => 'Users', 'action' => 'myAccount'])
.
Using Custom Validators
Often models will have multiple validator sets, you can have FormHelper
mark fields required based on the specific validator your controller
action is going to apply. For example, your Users table has specific validation
rules that only apply when an account is being registered:
echo $this->Form->create($user, [
'context' => ['validator' => 'register'],
]);
The above will use validation rules defined in the register
validator, which
are defined by UsersTable::validationRegister()
, for $user
and all
related associations. If you are creating a form for associated entities, you
can define validation rules for each association by using an array:
echo $this->Form->create($user, [
'context' => [
'validator' => [
'Users' => 'register',
'Comments' => 'default',
],
],
]);
The above would use register
for the user, and default
for the user’s
comments. FormHelper uses validators to generate HTML5 required attributes,
relevant ARIA attributes, and set error messages with the browser validator API
. If you would like to disable HTML5 validation messages use:
$this->Form->setConfig('autoSetCustomValidity', false);
This will not disable required
/aria-required
attributes.
Creating context classes
While the built-in context classes are intended to cover the basic cases you’ll
encounter you may need to build a new context class if you are using a different
ORM. In these situations you need to implement the
Cake\View\Form\ContextInterface . Once
you have implemented this interface you can wire your new context into the
FormHelper. It is often best to do this in a View.beforeRender
event
listener, or in an application view class:
$this->Form->addContextProvider('myprovider', function ($request, $data) {
if ($data['entity'] instanceof MyOrmClass) {
return new MyProvider($data);
}
});
Context factory functions are where you can add logic for checking the form
options for the correct type of entity. If matching input data is found you can
return an object. If there is no match return null.
Creating Form Controls
-
Cake\View\Helper\FormHelper::control(string $fieldName, array $options = [])
$fieldName
- A field name in the form 'Modelname.fieldname'
.
$options
- An optional array that can include both
Options for Control, and options of the other methods (which
control()
employs internally to generate various HTML elements) as
well as any valid HTML attributes.
The control()
method lets you generate complete form controls. These
controls will include a wrapping div
, label
, control widget, and validation error if
necessary. By using the metadata in the form context, this method will choose an
appropriate control type for each field. Internally control()
uses the other
methods of FormHelper.
Tip
Please note that while the fields generated by the control()
method are
called generically “inputs” on this page, technically speaking, the
control()
method can generate not only all of the HTML input
type
elements, but also other HTML form elements such as select
,
button
, textarea
.
By default the control()
method will employ the following widget templates:
'inputContainer' => '<div class="input {{type}}{{required}}">{{content}}</div>'
'input' => '<input type="{{type}}" name="{{name}}"{{attrs}}>'
'requiredClass' => 'required'
In case of validation errors it will also use:
'inputContainerError' => '<div class="input {{type}}{{required}} error">{{content}}{{error}}</div>'
The type of control created (when we provide no additional options to specify the
generated element type) is inferred via model introspection and
depends on the column datatype:
- Column Type
Resulting Form Field
- string, uuid (char, varchar, etc.)
text
- boolean, tinyint(1)
checkbox
- decimal
number
- float
number
- integer
number
- text
textarea
- text, with name of password, passwd
password
- text, with name of email
email
- text, with name of tel, telephone, or phone
tel
- date
date
- datetime, timestamp
datetime-local
- datetimefractional, timestampfractional
datetime-local
- time
time
- month
month
- year
select with years
- binary
file
The $options
parameter allows you to choose a specific control type if
you need to:
echo $this->Form->control('published', ['type' => 'checkbox']);
Tip
As a small subtlety, generating specific elements via the control()
form method will always also generate the wrapping div
, by default.
Generating the same type of element via one of the specific form methods
(e.g. $this->Form->checkbox('published');
) in most cases won’t generate
the wrapping div
. Depending on your needs you can use one or another.
The wrapping div
will have a required
class name appended if the
validation rules for the model’s field indicate that it is required and not
allowed to be empty. You can disable automatic required
flagging using the
'required'
option:
echo $this->Form->control('title', ['required' => false]);
To skip browser validation triggering for the whole form you can set option
'formnovalidate' => true
for the input button you generate using
Cake\View\Helper\FormHelper::submit()
or set 'novalidate' =>
true
in options for Cake\View\Helper\FormHelper::create()
.
For example, let’s assume that your Users model includes fields for a
username (varchar), password (varchar), approved (datetime) and
quote (text). You can use the control()
method of the FormHelper to
create appropriate controls for all of these form fields:
echo $this->Form->create($user);
// The following generates a Text input
echo $this->Form->control('username');
// The following generates a Password input
echo $this->Form->control('password');
// Assuming 'approved' is a datetime or timestamp field the following
//generates an input of type "datetime-local"
echo $this->Form->control('approved');
// The following generates a Textarea element
echo $this->Form->control('quote');
echo $this->Form->button('Add');
echo $this->Form->end();
A more extensive example showing some options for a date field:
echo $this->Form->control('birth_date', [
'label' => 'Date of birth',
'min' => date('Y') - 70,
'max' => date('Y') - 18,
]);
Besides the specific Options for Control,
you also can specify any option accepted by corresponding specific method
for the chosen (or inferred by CakePHP)
control type and any HTML attribute (for instance onfocus
).
If you want to create a select
form field while using a belongsTo (or
hasOne) relation, you can add the following to your UsersController
(assuming your User belongsTo Group):
$this->set('groups', $this->Users->Groups->find('list')->all());
Afterwards, add the following to your view template:
echo $this->Form->control('group_id', ['options' => $groups]);
To make a select
box for a belongsToMany Groups association you can
add the following to your UsersController:
$this->set('groups', $this->Users->Groups->find('list')->all());
Afterwards, add the following to your view template:
echo $this->Form->control('groups._ids', ['options' => $groups]);
If your model name consists of two or more words (e.g.
“UserGroups”), when passing the data using set()
you should name your
data in a pluralised and
lower camelCased
format as follows:
$this->set('userGroups', $this->UserGroups->find('list')->all());
Field Naming Conventions
When creating control widgets you should name your fields after the matching
attributes in the form’s entity. For example, if you created a form for an
$article
entity, you would create fields named after the properties. E.g.
title
, body
and published
.
You can create controls for associated models, or arbitrary models by passing in
association.fieldname
as the first parameter:
echo $this->Form->control('association.fieldname');
Any dots in your field names will be converted into nested request data. For
example, if you created a field with a name 0.comments.body
you would get
a name attribute that looks like 0[comments][body]
. This convention matches
the conventions you use with the ORM. Details for the various association types
can be found in the Creating Inputs for Associated Data section.
When creating datetime related controls, FormHelper will append a field-suffix.
You may notice additional fields named year
, month
, day
, hour
,
minute
, or meridian
being added. These fields will be automatically
converted into DateTime
objects when entities are marshalled.
Options for Control
FormHelper::control()
supports a large number of options via its $options
argument. In addition to its own options, control()
accepts options for the
inferred/chosen generated control types (e.g. for checkbox
or textarea
),
as well as HTML attributes. This subsection will cover the options specific to
FormHelper::control()
.
$options['type']
- A string that specifies the widget type
to be generated. In addition to the field types found in the
Creating Form Controls, you can also create 'file'
,
'password'
, and any other type supported by HTML5. By specifying a
'type'
you will force the type of the generated control, overriding model
introspection. Defaults to null
.
For example:
echo $this->Form->control('field', ['type' => 'file']);
echo $this->Form->control('email', ['type' => 'email']);
Output:
<div class="input file">
<label for="field">Field</label>
<input type="file" name="field" value="" id="field">
</div>
<div class="input email">
<label for="email">Email</label>
<input type="email" name="email" value="" id="email">
</div>
$options['label']
- Either a string caption or an array of
options for the label. You can set this key to the
string you would like to be displayed within the label that usually
accompanies the input
HTML element. Defaults to null
.
For example:
echo $this->Form->control('name', [
'label' => 'The User Alias'
]);
Output:
<div class="input">
<label for="name">The User Alias</label>
<input name="name" type="text" value="" id="name">
</div>
Alternatively, set this key to false
to disable the generation of the
label
element.
For example:
echo $this->Form->control('name', ['label' => false]);
Output:
<div class="input">
<input name="name" type="text" value="" id="name">
</div>
If the label is disabled, and a placeholder
attribute is provided, the
generated input will have aria-label
set.
Set the label
option to an array to provide additional options for the
label
element. If you do this, you can use a 'text'
key in
the array to customize the label text.
For example:
echo $this->Form->control('name', [
'label' => [
'class' => 'thingy',
'text' => 'The User Alias'
]
]);
Output:
<div class="input">
<label for="name" class="thingy">The User Alias</label>
<input name="name" type="text" value="" id="name">
</div>
$options['options']
- You can provide in here an array containing
the elements to be generated for widgets such as radio
or select
,
which require an array of items as an argument (see
Creating Radio Buttons and Creating Select Pickers for more details).
Defaults to null
.
$options['error']
- Using this key allows you to override the default
model error messages and can be used, for example, to set i18n messages. To
disable the error message output & field classes set the 'error'
key to
false
. Defaults to null
.
For example:
echo $this->Form->control('name', ['error' => false]);
To override the model error messages use an array with
the keys matching the original validation error messages.
For example:
$this->Form->control('name', [
'error' => ['Not long enough' => __('This is not long enough')]
]);
As seen above you can set the error message for each validation
rule you have in your models. In addition you can provide i18n
messages for your forms.
To disable the HTML entity encoding for error messages only, the 'escape'
sub key can be used:
$this->Form->control('name', [
'error' => ['escape' => false],
]);
$options['nestedInput']
- Used with checkboxes and radio buttons.
Controls whether the input element is generated
inside or outside the label
element. When control()
generates a
checkbox or a radio button, you can set this to false
to force the
generation of the HTML input
element outside of the label
element.
On the other hand you can set this to true
for any control type to force the
generated input element inside the label. If you change this for radio buttons
then you need to also modify the default
radioWrapper template. Depending on the generated
control type it defaults to true
or false
.
$options['templates']
- The templates you want to use for this input. Any
specified templates will be merged on top of the already loaded templates.
This option can be either a filename (without extension) in /config
that
contains the templates you want to load, or an array of templates to use.
$options['labelOptions']
- Set this to false
to disable labels around
nestedWidgets or set it to an array of attributes to be provided to the
label
tag.
$options['readonly']
- Set the field to readonly
in form.
For example:
echo $this->Form->control('name', ['readonly' => true]);
Generating Specific Types of Controls
In addition to the generic control()
method, FormHelper
has specific
methods for generating a number of different types of controls. These can be used
to generate just the control widget itself, and combined with other methods like
Cake\View\Helper\FormHelper::label()
and
Cake\View\Helper\FormHelper::error()
to generate fully custom
form layouts.
Common Options For Specific Controls
Many of the various control element methods support a common set of options which,
depending on the form method used, must be provided inside the $options
or
in the $attributes
array argument. All of these options are also supported
by the control()
method.
To reduce repetition, the common options shared by all control methods are
as follows:
'id'
- Set this key to force the value of the DOM id for the control.
This will override the 'idPrefix'
that may be set.
'default'
- Used to set a default value for the control field. The
value is used if the data passed to the form does not contain a value for the
field (or if no data is passed at all). If no default value is provided, the
column’s default value will be used.
Example usage:
echo $this->Form->text('ingredient', ['default' => 'Sugar']);
Example with select
field (size “Medium” will be selected as
default):
$sizes = ['s' => 'Small', 'm' => 'Medium', 'l' => 'Large'];
echo $this->Form->select('size', $sizes, ['default' => 'm']);
Note
You cannot use default
to check a checkbox - instead you might
set the value in $this->request->getData()
in your controller,
or set the control option 'checked'
to true
.
Beware of using false
to assign a default value. A false
value is
used to disable/exclude options of a control field, so 'default' => false
would not set any value at all. Instead use 'default' => 0
.
'value'
- Used to set a specific value for the control field. This
will override any value that may else be injected from the context, such as
Form, Entity or request->getData()
etc.
Note
If you want to set a field to not render its value fetched from
context or valuesSource you will need to set 'value'
to ''
(instead of setting it to null
).
In addition to the above options, you can mixin any HTML attribute you wish to
use. Any non-special option name will be treated as an HTML attribute, and
applied to the generated HTML control element.
Creating Labels
-
Cake\View\Helper\FormHelper::label(string $fieldName, string $text, array $options)
$fieldName
- A field name in the form 'Modelname.fieldname'
.
$text
- An optional string providing the label caption text.
$options
- Optional. Array containing any of the
Common Options For Specific Controls as well as any valid HTML attributes.
Creates a label
element. The argument $fieldName
is used for generating
the HTML for
attribute of the element; if $text
is undefined,
$fieldName
will also be used to inflect the label’s text
attribute.
For example:
echo $this->Form->label('name');
echo $this->Form->label('name', 'Your username');
Output:
<label for="name">Name</label>
<label for="name">Your username</label>
With the third parameter $options
you can set the id or class:
echo $this->Form->label('name', null, ['id' => 'user-label']);
echo $this->Form->label('name', 'Your username', ['class' => 'highlight']);
Output:
<label for="name" id="user-label">Name</label>
<label for="name" class="highlight">Your username</label>
Displaying and Checking Errors
FormHelper exposes a couple of methods that allow us to easily check for
field errors and when necessary display customized error messages.
Displaying Errors
-
Cake\View\Helper\FormHelper::error(string $fieldName, mixed $text, array $options)
$fieldName
- A field name in the form 'Modelname.fieldname'
.
$text
- Optional. A string or array providing the error message(s). If an
array, then it should be a hash of key names => messages. Defaults to
null
.
$options
- An optional array that can only contain a boolean with the key
'escape'
, which will define whether to HTML escape the
contents of the error message. Defaults to true
.
Shows a validation error message, specified by $text
, for the given
field, in the event that a validation error has occurred. If $text
is not
provided then the default validation error message for that field will be used.
Uses the following template widgets:
'error' => '<div class="error-message">{{content}}</div>'
'errorList' => '<ul>{{content}}</ul>'
'errorItem' => '<li>{{text}}</li>'
The 'errorList'
and 'errorItem'
templates are used to format mutiple
error messages per field.
Example:
// If in TicketsTable you have a 'notEmpty' validation rule:
public function validationDefault(Validator $validator): Validator
{
$validator
->requirePresence('ticket', 'create')
->notEmpty('ticket');
}
// And inside templates/Tickets/add.php you have:
echo $this->Form->text('ticket');
if ($this->Form->isFieldError('ticket')) {
echo $this->Form->error('ticket', 'Completely custom error message!');
}
If you would click the Submit button of your form without providing a value
for the Ticket field, your form would output:
<input name="ticket" class="form-error" required="required" value="" type="text">
<div class="error-message">Completely custom error message!</div>
Tip
If you use a certain model field to generate multiple form fields via
control()
, and you want the same validation error message displayed for
each one, you will probably be better off defining a custom error message
inside the respective validator rules.
Checking for Errors
-
Cake\View\Helper\FormHelper::isFieldError(string $fieldName)
Returns true
if the supplied $fieldName
has an active validation
error, otherwise returns false
.
Example:
if ($this->Form->isFieldError('gender')) {
echo $this->Form->error('gender');
}
Displaying validation messages in HTML5 validity messages
If the autoSetCustomValidity
FormHelper option is set to true
, error messages for
the field’s required and notBlank validation rules will be used in lieu of the default
browser HTML5 required messages. Enabling the option will add the onvalid
and oninvalid
event attributes to your fields, for example:
<input type="text" name="field" required onvalid="this.setCustomValidity('')" oninvalid="this.setCustomValidity('Custom notBlank message')">
If you want to manually set those events with custom JavaScript, you can set the autoSetCustomValidity
option to false
and use the special customValidityMessage
template variable instead. This
template variable is added when a field is required:
// example template
[
'input' => '<input type="{{type}}" name="{{name}}" data-error-message="{{customValidityMessage}}" {{attrs}}/>',
]
// would create an input like this
<input type="text" name="field" required data-error-message="Custom notBlank message" />
You could then use JavaScript to set the onvalid
and oninvalid
events as you like.
Creating Standalone Buttons and POST Links
Creating POST Buttons
-
Cake\View\Helper\FormHelper::postButton(string $title, mixed $url, array $options = [])
$title
- Mandatory string providing the button’s text caption. By default
not HTML encoded.
$url
- The URL of the form provided as a string or as array.
$options
- An optional array including any of the
Common Options For Specific Controls, or of the specific options (see below) as well
as any valid HTML attributes.
Creates a <button>
tag with a surrounding <form>
element that submits
via POST, by default. Also, by default, it generates hidden input fields for the
FormProtectionComponent.
Options for POST Button
'data'
- Array with key/value to pass in hidden input.
'method'
- Request method to use. E.g. set to 'delete'
to
simulate a HTTP/1.1 DELETE request. Defaults to 'post'
.
'form'
- Array with any option that FormHelper::create()
can take.
Also, the postButton()
method will accept the options which are valid for
the button()
method.
For example:
// In templates/Tickets/index.php
<?= $this->Form->postButton('Delete Record', ['controller' => 'Tickets', 'action' => 'delete', 5]) ?>
Will output HTML similar to:
<form method="post" accept-charset="utf-8" action="/Rtools/tickets/delete/5">
<div style="display:none;">
<input name="_method" value="POST" type="hidden">
</div>
<button type="submit">Delete Record</button>
<div style="display:none;">
<input name="_Token[fields]" value="186cfbfc6f519622e19d1e688633c4028229081f%3A" type="hidden">
<input name="_Token[unlocked]" value="" type="hidden">
<input name="_Token[debug]" value="%5B%22%5C%2FRtools%5C%2Ftickets%5C%2Fdelete%5C%2F1%22%2C%5B%5D%2C%5B%5D%5D" type="hidden">
</div>
</form>
Since this method generates a form
element, do not use this method in an
already opened form. Instead use
Cake\View\Helper\FormHelper::submit()
or Cake\View\Helper\FormHelper::button()
to create buttons
inside opened forms.
Creating POST Links
-
Cake\View\Helper\FormHelper::postLink(string $title, mixed $url = null, array $options = [])
$title
- Mandatory string providing the text to be wrapped in <a>
tags.
$url
- Optional. String or array which contains the URL
of the form (Cake-relative or external URL starting with http://
).
$options
- An optional array including any of the
Common Options For Specific Controls, or of the specific options (see below) as well
as any valid HTML attributes.
Creates an HTML link, but accesses the URL using the method you specify
(defaults to POST). Requires JavaScript to be enabled in browser:
// In your template, to delete an article, for example
<?= $this->Form->postLink(
'Delete',
['action' => 'delete', $article->id],
['confirm' => 'Are you sure?'])
?>
Options for POST Link
'data'
- Array with key/value to pass in hidden input.
'method'
- Request method to use. For example, setting it to 'delete'
will simulate a HTTP/1.1 DELETE request. Defaults to 'post'
.
'confirm'
- The confirmation message to display on click. Defaults to
null
.
'block'
- Set this option to true
to append the form to view block
'postLink'
or provide a custom block name. Defaults to null
.
Also, the postLink
method will accept the options which are valid for
the link()
method.
This method creates a <form>
element. If you want to use this method
inside of an existing form, you must use the block
option so that the
new form is being set to a view block that can be
rendered outside of the main form.
If all you are looking for is a button to submit your form, then you should
use Cake\View\Helper\FormHelper::button()
or
Cake\View\Helper\FormHelper::submit()
instead.
Note
Be careful to not put a postLink inside an open form. Instead use the
block
option to buffer the form into a view block