Sei sulla pagina 1di 15

Form API

Form API in Drupal 8.

Introduction to Form API


Use Drupal's Form API to consistently and efficiently produce and process forms in a module.

ConfigFormBase with Simple Configuration API


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

ConfirmFormBase To Confirm an Action


Learn to use the ConfirmFormBase class to quickly implement a confirmation dialog.

Form Render Elements


HTML5 and other useful form render elements
Introduction to Form API
Last updated on
30 December 2017

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:

 Consistent HTML output for all forms.


 Forms provided by one module can be easily altered by another without complex search and replace
logic.
 Complex form elements like file uploads and voting widgets can be encapsulated in reusable bundles
that include both display and processing logic.

There are several types of forms commonly used in Drupal. Each has a base class which you can extend in
your own custom module.

First, identify the type of form you need to build:

1. A generic form. Extend FormBase.


2. A configuration form that enables administrators update a module's settings.
Extend ConfigFormBase.
3. A form for deleting content or configuration which provides a confirmation step.
Extend ConfirmFormBase.

FormBase implements FormInterface, and both ConfigFormBase and ConfirmFormBase extend


FormBase, therefore any forms that extend these classes must implement a few required methods.

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()

public function 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:

public function getFormId() {


return 'mymodule_settings';
}

buildForm()

public function buildForm(array $form, FormStateInterface $form_state)

This returns a Form API array that defines each of the elements your form is composed of.

Example:

public function buildForm(array $form, FormStateInterface $form_state) {


$form['phone_number'] = array(
'#type' => 'tel',
'#title' => 'Example phone',
);
return $form;
}

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.

The following is an example of a simple validateForm() method:

/**
* {@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.

Here is a complete example of a form class:

File contents of /modules/example/src/Form/ExampleForm.php if the module is in /modules/example:

<?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.

Integrate the form in a request

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:

File contents for /modules/example/example.routing.yml if the module is in /modules/example:

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.

Retrieving this form outside of routes

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 this form

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

2. In your_module.routing.yml file, you define the route:

...
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'

3. in your_module/src/Form/ModuleConfigurationForm.php you define the form:

<?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:

 public function submitForm(array &$form, FormStateInterface $form_state);


 public function getFormId();
 public function getCancelUrl();
 public function getQuestion();

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

This documentation is incomplete. Add more information.

HTML5 Form Render Elements

The HTML5 elements are:

 '#type' => 'tel'


 '#type' => 'email'
 '#type' => 'number'
 '#type' => 'date'
 '#type' => 'url'
 '#type' => 'search'
 '#type' => 'range'

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.

Other useful render elements

 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'

Note: Named parameters are not sanitized by default!


This means that you really should sanitize all values prior to use. You can somewhat decrease a code
injection security concern by specifying regex requirements:

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.
}
}

This works because

1. $user is an instance of Drupal\user\Entity\User and that object implements AccountInterface.


Type hinting with Drupal\user\UserInterface or even Drupal\Core\Entity\EntityInterface
or any other interface the User class extends would make the argument passing work as well.
(Typehinting with classes work as well up to the User class itself but it's best practice to typehint
with interfaces.)
2. When typehinting with Request the request object is automatically passed in (even the argument
name does not matter). Note a few Request quirks: the $_GET superglobal can be accessed in
$request->query (and it's a ParameterBag, not an array) while the session information can be
accessed via $request->getSession() which returns a session object with its own methods.

Note: this works irrespective of the order of the method arguments.


In this case, if the URL includes the user id of a non-existent user, this will return a 404 Not Found response
automatically.

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;

class BasicForm extends FormBase {

public function buildForm(array $form, FormStateInterface $form_state,


AccountInterface $user = NULL) {
// Do something with $user in the form
}
}

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.

Note: For form arguments, it is essential to provide a default value


for each argument!
In the example above, $user is given a default value of NULL. Without this, your code will not compile.

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'

Potrebbero piacerti anche