Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Overview
Form classes implement the \Drupal\Core\Form\FormInterface and the basic workflow of a form is defined
by the buildForm, validateForm, and submitForm methods of the interface. When a form is requested it's
defined as a renderable array often referred to as a Form API array or simply $form array. The $form array
is converted to HTML by the render process and displayed to the end user. When a user submits a form the
request is made to the same URL that the form was displayed on, Drupal notices the incoming HTTP POST
data in the request and this time instead of building the form and displaying it as HTML, it builds the form
and then proceeds to call the applicable validation and submission handlers.
Defining forms as structured arrays instead of straight HTML has many advantages including:
There are several types of forms commonly used in Drupal. Each has a base class which you can extend in
your own custom module.
Required Methods
FormBase implements FormInterface, and therefore any form that has FormBase in its hierarchy is
required to implement a few methods:
getFormId()
buildForm()
validateForm()
submitForm()
getFormId()
This needs to return a string that is the unique ID of your form. Namespace the form ID based on your
module's name.
Example:
buildForm()
This returns a Form API array that defines each of the elements your form is composed of.
Example:
Validating Forms
After a user fills out the form and clicks the submit button it's common to want to perform some sort of
validation on the data that's being collected. To do this with Drupal's Form API we simply implement the
validateForm method from \Drupal\Core\Form\FormInterface in our ExampleForm class.
User submitted values from the form are contained in the $form_state object at $form_state-
>getValue('field_id'), where 'field_id' is the key used when adding the form element to the $form array in
FormExample::buildForm(). We can perform our custom validation on this value. If you need to get all
submitted values, you can do so by using $form_state->getValues().
Form validation methods can use any PHP processing necessary to validate that the field contains the
desired value and raise an error in the event that it is an invalid value. In this case since we're extending the
\Drupal\Core\Form\FormBase class we can use \Drupal\Core\Form\FormStateInterface::setErrorByName()
to register an error on a specific form element and provide an associated message explaining the error.
When a form is submitted, Drupal runs through all validation handlers for the form, both the default
validation handlers, and any validation handlers added by developers. If there are any errors, the HTML for
the form is rebuilt, error messages are shown, and fields with errors are highlighted. This allows the user to
correct any errors and re-submit the form. If no errors are present, the submit handlers for the form are
executed.
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (strlen($form_state->getValue('phone_number')) < 3) {
$form_state->setErrorByName('phone_number', $this->t('The phone number is too
short. Please enter a full phone number.'));
}
}
If no errors are registered during form validation then Drupal continues on with processing the form. At this
point it is assumed that values within $form_state->getValues() are valid and ready to be processed and
used in whatever way our module needs to make use of the data.
Submitting Forms / Processing Form Data
Finally, we're ready to make use of the data that we've collected and do things like save it to the database or
send an email or any number of other operations. To do this with Drupal's Form API we need to implement
the submitForm method from \Drupal\Core\Form\FormInterface in our ExampleForm class.
Just like in the validation method above the values collected from the user when the form was submitted are
in $form_state->getValues() and at this point we can assume they've been validated and are ready for us
to make use of. Accessing the value of our 'phone_number' field can be done by accessing $form_state-
>getValue('phone_number').
Here's an example of a simple submitForm method which displays the value of the 'phone_number' field on
the page using drupal_set_message():
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
drupal_set_message($this->t('Your phone number is @number', array('@number' =>
$form_state->getValue('phone_number'))));
}
This is a really simple example of handling submitted form data. For more complex examples take a look at
some of the classes that extend FormBase in core.
<?php
namespace Drupal\example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements an example form.
*/
class ExampleForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'example_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['phone_number'] = array(
'#type' => 'tel',
'#title' => $this->t('Your phone number'),
);
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (strlen($form_state->getValue('phone_number')) < 3) {
$form_state->setErrorByName('phone_number', $this->t('The phone number is too
short. Please enter a full phone number.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
drupal_set_message($this->t('Your phone number is @number', array('@number' =>
$form_state->getValue('phone_number'))));
}
The id of the form is returned by the getFormId() method on the form class. The builder method is called
buildForm() and there are dedicated methods for validation and submission.
The routing system allows form classes to be provided as route handlers, in which case the route system
takes care of instantiating this class and invoking the proper methods. To integrate this form into a Drupal
site's URI structure, use a route like the following:
example.form:
path: '/example-form'
defaults:
_title: 'Example form'
_form: '\Drupal\example\Form\ExampleForm'
requirements:
_permission: 'access content'
The _form key tells the routing system that the provided class name is a form class to be instantiated and
handled as a form.
Note that the form class and the routing entry are the only two pieces required to make this form work, there
is no other wrapper code to write.
Although Drupal 7's drupal_get_form() is gone in Drupal 8, there is a FormBuilder service that can be
used to retrieve and process forms. The Drupal 8 equivalent of drupal_get_form() is the following:
$form = \Drupal::formBuilder()->getForm('Drupal\example\Form\ExampleForm');
The argument passed to the getForm() method is the name of the class that defines your form and is an
implementation of \Drupal\Core\Form\FormInterface. If you need to pass any additional parameters to the
form, pass them on after the class name.
Example:
$extra = '612-123-4567';
$form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);
...
public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
$form['phone_number'] = array(
'#type' => 'tel',
'#title' => $this->t('Your phone number'),
'#value' => $extra,
);
return $form;
}
In some special cases, you may need to manipulate the form object before the FormBuilder calls your
classes buildForm() method, in which case you can do $form_object = new
\Drupal\mymodule\Form\ExampleForm($something_special); $form_builder-
>getForm($form_object);
Altering forms is where the Drupal 8 Form API reaches into basically the same hook-based approach as
Drupal 7. Use hook_form_alter() and/or hook_form_FORM_ID_alter() to alter the form where the ID is the
form ID you provided when defining the form previously.
/**
* Implements hook_form_FORM_ID_alter().
*/
function example2_form_example_form_alter(&$form, \Drupal\Core\Form\FormStateInterface
$form_state) {
$form['phone_number']['#description'] = t('Start with + and your country code.');
}
We named the hook_form_FORM_ID_alter() implementation after our module name (example2) including
the form ID (example_form). This is the same pattern used in Drupal 7 form alters.
ConfigFormBase with Simple Configuration API
Last updated on
10 September 2017
This type of form is used to create forms for configuration pages on your Drupal website. You can set up
configuration forms that allow you to make changes to features or views or other configuration entities.
Configuration forms can greatly help you to understand the workings of the Simple Configuration API for
Drupal 8. In Drupal 8, you do not use the {variables} table and variable_get/set/delete() because
configuration is stored the database and synced with YML files on the disk for deployment purposes. It is
possible to disable use of the database for config storage entirely but it comes with a performance hit with
most filesystems.
The Simple Configuration API enables using objects like $config objects to communicate with a YML file.
The $config object handles CRUD (Create/Read/Update/Delete) for YML files, so you simply use ::get(),
::set(), and ::save() methods and your data will be stored in the {module}.settings.yml file.
Example
1. In your_module.info.yml file, you define the configuration route:
...
configure: your_module.admin_settings
...
your_module.admin_settings:
path: '/admin/config/your_module'
defaults:
_form: '\Drupal\your_module\Form\ModuleConfigurationForm'
_title: 'your_module configuration screen'
requirements:
_permission: 'administer site configuration'
<?php
namespace Drupal\your_module\Form;
use Drupal\Core\Form\ConfigFormBase;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines a form that configures forms module settings.
*/
class ModuleConfigurationForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'your_module_admin_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'your_module.settings',
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, Request
$request = NULL) {
$config = $this->config('your_module.settings');
$form['your_message'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your message'),
'#default_value' => $config->get('your_message'),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$this->config('your_module.settings')
->set('your_message', $values['your_message'])
->save();
}
}
ConfirmFormBase To Confirm an Action
Last updated on
24 July 2017
Confirmation forms are fairly simple to build and are the recommended way to prompt a user for
confirmation of an action. As with many forms, it all begins with a route.
Routes
Within a module's *.routing.yml file, create a route to the form. In many cases, it will be desirable to pass a
parameter from the path to the confirmation form, as would be the case some kind of content was being
deleted. This can be seen in the following example:
example_module.delete:
path: '/example/{id}/delete'
defaults:
_form: '\Drupal\example_module\Form\ConfirmDeleteForm'
_title: 'Confirm Deletion'
requirements:
_permission: 'administer site configuration'
id: ^\d+$
The value of id is passed to the form's buildForm() function via a parameter appended to the standard
parameter list. A regex to only allow numeric ids to be passed has been applied under the 'requirements'
section.
Note: The route parameters are user supplied content and is therefore NOT SAFE. The regex above
guarantees that only numerics are passed along but other parameters will likely need to be sanitized or
verified in some way to ensure malicious content doesn't get passed along.
ConfirmFormBase
Construct a new form that extends ConfirmFormBase and implements ConfirmFormInterface. At minimum,
the following four functions from ConfirmFormInterface need to get implemented:
To see what else you can implement check the ConfirmFormInterface API document.
In order to get the route parameter for use within the form you will need to create a field in the class to store
it and you will need to implement buildForm() with an additional parameter for the route parameter.
Example
<?php
/**
* @file
* Contains \Drupal\example_module\Form\ConfirmDeleteForm.
*/
namespace Drupal\example_module\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\ConfirmFormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Defines a confirmation form to confirm deletion of something by id.
*/
class ConfirmDeleteForm extends ConfirmFormBase {
/**
* ID of the item to delete.
*
* @var int
*/
protected $id;
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, string $id =
NULL) {
$this->id = $id;
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
/* do the deletion */
}
/**
* {@inheritdoc}
*/
public function getFormId() : string {
return "confirm_delete_form";
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('example_module.another_path');
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return t('Do you want to delete %id?', ['%id' => $this->id]);
}
This example is fairly minimalistic and there are many ways it could be improved. Within Drupal, using the
Bartik theme, the result would look like this:
Related Content
Using parameters in routes
Use a named parameter if you need to refer to a content or configuration entity type in the route path. For
example in example.routing.yml:
Form Render Elements
Last updated on
20 October 2017
Using these elements as opposed to requesting data in plain textfields is preferable because devices can pull
up the proper input methods for them, such as when a telephone number is requested, a keypad screen would
display on a device.
details: The '#type' => 'details' element is a grouping element with a summary.
language_select: The '#type' => 'language_select' element is a language selector to make it
easy to put language configuration on forms.
dropbutton: The '#type' => 'dropbutton' provides a generic select list with a dropbutton. See
also the '#type' => 'operations' element.
operations: The '#type' => 'operations' element provides a list of available operations, which
you can commonly find in entity configuration administrative screens, such as at
admin/structure/types.
Other entities with autocomplete: The '#type' => 'entity_autocomplete' element allow to
select an entity of a specific type.
Check out namespace Drupal\Core\Render\Element for all the core provided elements.
Using parameters in routes
Last updated on
21 December 2017
Use a named parameter if you need to refer to a content or configuration entity type in the route path. For
example in example.routing.yml:
example.user:
path: 'example/{user}'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_permission: 'access content'
example.user:
path: 'example/{user}'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_permission: 'access content'
user: '^[a-zA-Z0-9_]+'
There is nothing more to provide in the route for the parameter conversion to happen because user is the
name of an entity type and it will get automatically converted into a user object. In the controller, define an
argument on the controller method with the same name to get this user object passed in:
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;
class ExampleController {
// ...
public function content(AccountInterface $user, Request $request) {
// Do something with $user.
}
}
Forms can use upcasted URL parameters too. In this example a form is using the account object and its ID is
passed in from the URL.
example.user_form:
path: 'example/form/{user}'
defaults:
_form: '\Drupal\example\Form\ExampleForm'
requirements:
_permission: 'access content'
namespace Drupal\example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Session\AccountInterface;
Note that this method of providing the entity data to a form is not used for entity forms, such as forms to
add/edit/delete entity data.
If two entities of the same type are required in the same route then the typehinting magic above won't work
and the slug-to-argument name magic as explained in the parameters in routes handbook page needs to be
used. However, upcasting still works, just the system needs a little help:
route_with_two_nodes:
path: '/foo/{node1}/{node2}'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::foo'
options:
parameters:
node1:
type: entity:node
node2:
type: entity:node
use Drupal\node\NodeInterface;
class ExampleController {
function foo(NodeInterface $node1, NodeInterface $node2) {
}
}
Optional parameters
Parameters on routes can be omitted when a default value for the parameter is supplied. Imagine you have a
form controller that allows people to report different issues (e.g. bug reports, feature requests and support
requests), and if the type is omitted it should default to 'support request'. Supply the default value for the
optional parameter in the "defaults" section:
issue.report_form:
path: 'report/{issue_type}'
defaults:
_controller: '\Drupal\issue\Controller\IssueController::report'
issue_type: 'support-request'
requirements:
_permission: 'report issue'
Now if we do a request to 'report' the $issue_type parameter will default to 'support_request'. We can
override the value by supplying it on the URL, like 'report/bug'.
The default values for arguments can also be used to provide routes with fixed paths to controllers that
expect arguments. Imagine for example that our SEO expert finds it extremely important that our form to
submit bug reports is available on the path 'report-a-bug'. We can reuse the same controller as in the
previous example, and provide a different default for "issue_type". The router knows that this parameter
exists and will pass it on to the controller:
issue.report_a_bug:
path: 'report-a-bug'
defaults:
_controller: '\Drupal\issue\Controller\IssueController::report'
issue_type: 'bug'
requirements:
_permission: 'report issue'