官术网_书友最值得收藏!

The form

So, now, our page displays a greeting dynamically depending on the time of day. However, we now want an administrator to specify what the greeting should actually be, in other words, to override the default behavior of our salutation if they so choose.

The ingredients for achieving this will be as follows:

  • A route (a new page) that displays a form that the administrator can add the greeting with
  • A configuration object that will store the greeting

In building this functionality, we will also take a look at how to add a dependency to our existing service. So, let's get started with our new route that naturally goes inside the hello_world.routing.yml file:

hello_world.greeting_form:
path: '/admin/config/salutation-configuration'
defaults:
_form: '\Drupal\hello_world\Form\SalutationConfigurationForm'
_title: 'Salutation configuration'
requirements:
_permission: 'administer site configuration'

Most of this route definition is the same as we saw earlier. There is one change, though, that it maps to a form instead of a Controller. This means that the entire page is a form page. Also, since the path is within the administration space, it will use the administration theme of the site. What is left to do now is to create our form class inside the /Form folder of our namespace (a standard practice directory for storing forms, but not mandatory).

Due to the power of inheritance, our form is actually very simple. However, I will explain what goes on in the background and guide you on your path to building more complex forms. So, here we have our form:

namespace Drupal\hello_world\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Configuration form definition for the salutation message.
*/
class SalutationConfigurationForm extends ConfigFormBase {

/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['hello_world.custom_salutation'];
}

/**
* {@inheritdoc}
*/
public function getFormId() {
return 'salutation_configuration_form';
}

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('hello_world.custom_salutation');

$form['salutation'] = array(
'#type' => 'textfield',
'#title' => $this->t('Salutation'),
'#description' => $this->t('Please provide the salutation you want to use.'),
'#default_value' => $config->get('salutation'),
);

return parent::buildForm($form, $form_state);
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('hello_world.custom_salutation')
->set('salutation', $form_state->getValue('salutation'))
->save();

parent::submitForm($form, $form_state);
}
}

Before going into the explanation, I should say that this is it. Clearing the cache and navigating to admin/config/salutation-configuration will present you with your simple configuration form via which you can save a custom salutation message:

Later on, we will make use of that value. However, first, let's talk a bit about forms in general, and then this form in particular.

A form in Drupal 8 is represented by a class that implements FormInterface. Typically, we either extend from FormBase or from ConfigFormBase, depending on what its purpose is. In this case we created a configuration form so we extended from the latter class.

There are four main methods that come into play in this interface:

  • getFormId(): Returns a unique, machine-readable name for the form.
  • buildForm(): Returns the form definition (an array of form element definitions and some extra metadata, as needed).
  • validateForm(): The handler that gets called to validate the form submission. It receives the form definition and a $form_state object that contains, among others, the submitted values. You can flag invalid values on their respective form elements, which means that the form is not submitted but refreshed (with the offending elements highlighted).
  • submitForm(): The handler that gets called when the form is submitted (if validation has passed without errors). It receives the same arguments as validateForm(). You can perform operations, such as saving the submitted values, or trigger some other kind of flow.

Defining a form, in a nutshell, means creating an array of form element definitions. The resulting form is very similar to the render array we mentioned earlier and which we will describe in more depth in the Chapter 5, Creating Your First Module. When creating your forms, you have a large number of form element types to use. A complete reference of what they are and what their options are (their definition specificities) can be found on the Drupal Form API reference page (https://api.drupal.org/api/drupal/elements/8.2.x). Keep this page close to you throughout your Drupal 8 development.

From a dependency injection point of view, forms can receive arguments from the Service Container in the same way we injected the salutation service into our Controller. As a matter of fact, ConfigFormBase ,which we are extending in our preceding form above, injects the config.factory service because it needs to use it for reading and storing configuration values. This is primarily why we extend from that form--so that we don't have to write this code ourselves. Drupal is full of these helpful classes we can extend and provides a bunch of useful boilerplate techniques that are very commonly used across the Drupal ecosystem.

If the form you are building is not storing or working with your configuration, you will typically extend from FormBase, which provides some static methods and traits and also implements some interfaces. The same word of caution goes for its helper service methods as for the ControllerBase.

Let's turn to our preceding form class and dissect it a bit now that we know a thing or two about forms.

Check the getFormId() method that we have now. We also have a buildForm() and submitForm(), but not validateForm(). The latter is not mandatory, and we don't actually need it for our example, but if we did, we could have something like this:

  /**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$salutation = $form_state->getValue('salutation');
if (strlen($salutation) > 20) {
$form_state->setErrorByName('salutation', $this->t('This salutation is too long'));
}
}

In this validation handler, we basically check whether the submitted value for the salutation element is longer than 20 characters. If so, we set an error on that element (to turn it red usually) and specify an error message on the form state specific to this error. The form will then be refreshed and the error will be presented, and the submit handler, in this case, will not be called.

For the purposes of our example, this is, however, not necessary, so I will not include it in the final code.

If we return to our form class, we will also see a strange getEditableConfigNames() method. This is required by the ConfigFormBaseTrait, which is used in the ConfigFormBase class that we are extending, and it needs to return an array of configuration object names that this form needs and which this form intends to edit. This is because there are two ways of loading configuration objects: editable and immutable. With this method, we inform it that we want to edit that configuration item.

As we see on the first line of buildForm(), we are using the config() method of the above-mentioned trait to load up our editable configuration object from the Drupal 8 configuration factory. This is to check the value that is currently stored in it. Then, we define our form elements (one, in our case, a simple text field). As #default_value (the value present in the element when the user goes to the form) on that form element, we will put whatever is in the configuration object. The rest of the element options are self-explanatory and pretty standard across all element types. Consult the Form API reference to see what other options are available and for which element types. Finally, at the end of the method, we also called the parent method because that provides the form's submit button, which for our purposes is enough.

The last method we wrote is the submit handler, which basically loads up the editable configuration object, puts the submitted value in it, and saves it. Finally, it also calls the parent method, which then simply sets a success message to the user on the screen using drupal_set_message()--a standard way of showing the user success or error messages from a code context.

That is pretty much it; this will work just fine.

From the point of view of configuration, we used ConfigFormBase to make our lives easier and combine the form aspect with that of the configuration storage. In a later chapter, we will talk a bit more about the different types of storage and also cover how to work with the configuration objects in more detail, as well as what these entail.

主站蜘蛛池模板: 鄂托克旗| 青阳县| 临洮县| 濮阳市| 彭山县| 天等县| 灌云县| 桑植县| 广饶县| 齐齐哈尔市| 明溪县| 乌兰察布市| 嘉祥县| 交口县| 麟游县| 金塔县| 庆安县| 台中市| 霞浦县| 荃湾区| 鄂州市| 和田县| 顺昌县| 越西县| 金沙县| 嵊泗县| 云阳县| 屏东县| 无为县| 耒阳市| 曲阳县| 新野县| 望谟县| 清远市| 盈江县| 宝应县| 民权县| 蓬溪县| 林周县| 河池市| 竹溪县|