Home >Backend Development >PHP Tutorial >Building a Drupal 8 Module: Blocks and Forms

Building a Drupal 8 Module: Blocks and Forms

Jennifer Aniston
Jennifer AnistonOriginal
2025-02-21 08:45:09543browse

Building a Drupal 8 Module: Blocks and Forms

Core points

  • Drupal 8 introduces a new concept of blocks as a plugin, allowing them to be reused throughout the site. Blocks can be created in the UI and used multiple times.
  • In Drupal 8, you can create a custom block that returns configurable text. This can be done by creating a new block type in the UI and reusing it throughout the site.
  • Drupal 8 allows adding configuration forms to blocks. This enables the user to edit the block, specify a name in the text field, and the block will then display a greeting to that name.
  • Drupal 8 module development involves creating a simple form. Form definition functions are grouped together in a class. Submitted form values ​​are simply printed on the screen to show how they work.
  • In Drupal 8, you can create custom forms by defining form classes in custom modules. The form class should extend the "FormBase" class and implement three methods: "getFormId()", "buildForm()", and "submitForm()".

Note that some code parts may be outdated due to the ongoing development process of Drupal 8 at the time of writing. Please check out this repository, I tried updating the sample code and making it work with the latest Drupal 8 version.

In the first part of this series, we start with the basics and learn Drupal 8 module development. We've learned how Drupal understands the files required by our module, how the routing process works, and how to create menu links programmatically in a configuration manner.

In this tutorial, we will further look at the sandbox modules found in this repository and look at two important features: blocks and forms. To do this, we will create a custom block that returns some configurable text. After that, we will create a simple form for printing the user-submitted values ​​to the screen.

Drupal 8 Block

A cool new change to the Block API in D8 is to make blocks more prominent by making them as plugins (a whole new concept). This means they are reusable features (in the background), as you can now create a block in the UI and reuse it throughout the site – you are no longer limited to using blocks only once.

Let's create a simple block type that will print to the screen by default Hello World!. We only need to use a class file in the src/Plugin/Block folder located in the root of the module. Let's name our new block type DemoBlock, of course it needs to be in a file named DemoBlock.php. In this file we can start with the following:

<code class="language-php"><?php
namespace Drupal\demo\Plugin\Block;

use Drupal\block\BlockBase;
use Drupal\Core\Session\AccountInterface;

/**
 * Provides a 'Demo' block.
 *
 * @Block(
 *   id = "demo_block",
 *   admin_label = @Translation("Demo block"),
 * )
 */

class DemoBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return array(
      '#markup' => $this->t('Hello World!'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function access(AccountInterface $account) {
    return $account->hasPermission('access content');
  }

}</code>

Like all other class files, we first namespace our class. Then we use the BlockBase class so that we can extend it, and the AccountInterface class so that we can access the currently logged in user. Next is something you certainly haven't seen in Drupal 7: Comments.

Annotations are PHP discovery tools located in comment blocks in files that are the same as class definitions. Using these annotations, we let Drupal know that we want to register a new block type (@Block) with the ID demo_block and the admin_label with

Demo block (passed through the translation system).

Next, we extend the BlockBase class to our own DemoBlock, where we implement two methods (the most common methods you will implement). The build() method is most important because it returns the renderable array that the block will print out. The access() method controls access to view this block. The argument passed to it is an instance of the AccountInterface class, in which case it is the current user.

Another interesting thing to note is that we no longer use the t() function globally for translation, but instead refer to the t() method implemented in the class parent class.

That's it, you can clear the cache and go to the Block Layout configuration page. The cool thing is that you have block types on the right (you can filter through them) where you can place one or more of these types of blocks into various areas of the site.

Drupal 8 Block Configuration

Now that we have learned how to create a new block type to use in the UI, let's dig deeper into the API and add configuration forms to it. We will enable it to edit the block, specify a name in the text field, and the block will say hello to that name, rather than to say hello to

world.

First, we need to define a form containing our text fields. So, in our DemoBlock class, we can add a new method called blockForm():

<code class="language-php">/**
 * {@inheritdoc}
 */
public function blockForm($form, &$form_state) {

  $form = parent::blockForm($form, $form_state);

  $config = $this->getConfiguration();

  $form['demo_block_settings'] = array(
    '#type' => 'textfield',
    '#title' => $this->t('Who'),
    '#description' => $this->t('Who do you want to say hello to?'),
    '#default_value' => isset($config['demo_block_settings']) ? $config['demo_block_settings'] : '',
  );

  return $form;
}</code>
This form API implementation should look very similar to Drupal 7. However, there is some new content here. First, we retrieve the $form array from the parent class (so we build an existing form by adding our own fields). Standard OOP stuff. We then retrieve and store the configuration of this block. The BlockBase class defines the getConfiguration() method that does this for us. We place the demo_block_settings value as #default_value in case it has been set.

Next, is the submission handler for this form, which will process the value of our field and store it in the chunk's configuration:

<code class="language-php">/**
* {@inheritdoc}
*/
public function blockSubmit($form, &$form_state) {

 $this->setConfigurationValue('demo_block_settings', $form_state['values']['demo_block_settings']);

}</code>
This method is also located in the DemoBlock class, and all it does is save the value of the demo_block_settings field as a new item in the block configuration (using the same name as the key for consistency).

Finally, we need to adjust our build() method to include the name to ask:

<code class="language-php"><?php
namespace Drupal\demo\Plugin\Block;

use Drupal\block\BlockBase;
use Drupal\Core\Session\AccountInterface;

/**
 * Provides a 'Demo' block.
 *
 * @Block(
 *   id = "demo_block",
 *   admin_label = @Translation("Demo block"),
 * )
 */

class DemoBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return array(
      '#markup' => $this->t('Hello World!'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function access(AccountInterface $account) {
    return $account->hasPermission('access content');
  }

}</code>

So far, this should look pretty easy. We are searching for the configuration of the block and use it for the printed statement if the value of our field has been set. If not, a common statement is used. You can clear the cache and test it by editing the blocks you assigned to the zone and adding the name you want to ask for. One thing to note is that you still have the responsibility to clean up user input when printing to the screen. For brevity, I did not include these steps.

Drupal 8 Form

The last thing we're going to explore in this tutorial is how to create a simple form. Due to space limitations, I will not cover its configuration management aspect (stores configuration values ​​submitted through forms). Instead, I'll illustrate a simple form definition where the submitted value is simply printed on the screen to show how it works.

In Drupal 8, form definition functions are grouped in a class. So let's define our simple DemoForm class in src/Form/DemoForm.php:

<code class="language-php">/**
 * {@inheritdoc}
 */
public function blockForm($form, &$form_state) {

  $form = parent::blockForm($form, $form_state);

  $config = $this->getConfiguration();

  $form['demo_block_settings'] = array(
    '#type' => 'textfield',
    '#title' => $this->t('Who'),
    '#description' => $this->t('Who do you want to say hello to?'),
    '#default_value' => isset($config['demo_block_settings']) ? $config['demo_block_settings'] : '',
  );

  return $form;
}</code>

Apart from the OOP aspect, everything should be very similar to Drupal 7. The Form API has hardly changed (except for adding some new form elements and such encapsulation). So what happened above?

First, we namespace the class and use the core FormBase class so that we can extend it with our own DemoForm class. Then we implement 4 methods, 3 of which should look very familiar. The getFormId() method is new and mandatory and is only used to return the machine name of the form. The buildForm() method is again mandatory, which builds the form. how? Just like you're used to from Drupal 7. The validateForm() method is optional and its purpose should also be very clear from D7. Finally, the submitForm() method performs the commit processing. Very logical and organized.

So what do we want to achieve with this form? We have an email field (a new form element in Drupal 8) that we want the user to fill in. By default, Drupal checks whether the entered value is actually an email address. But in our verification function we make sure it is a .com email address and if it is not, we will set the form error on that field. Finally, the submission handler simply prints a message on the page.

The last thing we need to do in order to use this form is to provide it with a route. So edit the demo.routing.yml file and add the following:

<code class="language-php">/**
* {@inheritdoc}
*/
public function blockSubmit($form, &$form_state) {

 $this->setConfigurationValue('demo_block_settings', $form_state['values']['demo_block_settings']);

}</code>

This should be similar to what we routed simple pages in the previous post. The only significant difference is that under defaults, we use _form to specify that the target is the form class. Therefore, the value is the class name we just created.

Clear the cache and navigate to demo/form to view the form and test it.

If you are familiar with drupal_get_form() and want to know how to load forms like you did in Drupal 7 before, the answer is in the global Drupal class. So to retrieve a form, you can use its formBuilder() method and do the following:

<code class="language-php"><?php
namespace Drupal\demo\Plugin\Block;

use Drupal\block\BlockBase;
use Drupal\Core\Session\AccountInterface;

/**
 * Provides a 'Demo' block.
 *
 * @Block(
 *   id = "demo_block",
 *   admin_label = @Translation("Demo block"),
 * )
 */

class DemoBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return array(
      '#markup' => $this->t('Hello World!'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function access(AccountInterface $account) {
    return $account->hasPermission('access content');
  }

}</code>

You can then return $form, which will be a renderable array of the form.

Conclusion

In this article, we continue to explore Drupal 8 module development and introduce two new topics: blocks and forms. We have learned how to create our own block type, which we can use to create blocks in the UI. We also learned how to add custom configurations to it and store values ​​for later use. Regarding forms, we have seen a simple implementation of the FormBase class, which we use to print the user-submitted values ​​to the screen.

In the next tutorial, we will quickly learn about the configuration form. We will use the Drupal 8 configuration system to save user-submitted values. Additionally, we will learn about service containers and dependency injections and how they work in Drupal 8. See you then.

Frequently Asked Questions about Building Drupal 8 Modules: Blocks and Forms (FAQ)

What is the basic structure of Drupal 8 module?

The Drupal 8 module is essentially a set of files containing certain functions and is written in PHP. The basic structure of the Drupal 8 module includes .info.yml file, .module file, and other optional files, such as .css, .js, .twig, etc. The .info.yml file is used to list the module's name, description, package, type, and core compatibility. The .module file is where the actual PHP code is located.

How to create custom blocks in Drupal 8?

Creating a custom block in Drupal 8 involves creating a new custom module and defining a block plugin in it. The block plugin is a PHP class file that defines the properties and methods of the block. It should be placed in the "src/Plugin/Block" directory of the module. The block plugin class should extend the "BlockBase" class and implement the "build()" method, which returns a renderable array of block content.

How to create a custom form in Drupal 8?

Creating a custom form in Drupal 8 involves creating a new custom module and defining a form class there. The form class is a PHP class file that defines the properties and methods of the form. It should be placed in the "src/Form" directory of the module. The form class should extend the "FormBase" class and implement three methods: "getFormId()", "buildForm()", and "submitForm()". The "buildForm()" method returns the form array, and the "submitForm()" method handles the form submission.

How to display blocks in specific areas of my Drupal 8 site?

To display blocks in a specific area of ​​the Drupal 8 site, you need to go to the Block Layout page in the management interface. Here you can assign your block to any area of ​​the topic. You can also configure the visibility settings of blocks based on paths, content types, user roles, etc.

How to verify input of Drupal 8 custom form?

To verify input to a Drupal 8 custom form, you can override the "validateForm()" method in the form class. When submitting the form, this method is called before the "submitForm()" method. In the "validateForm()" method, you can add validation logic and call the "setError()" method to set the error message for the form element (if validation fails).

How to change an existing form in Drupal 8?

To change an existing form in Drupal 8, you can implement the "hook_form_FORM_ID_alter()" function in the module. This function is called when building a form, which allows you to modify the form array. "FORM_ID" should be replaced with the ID of the form you want to change.

How to programmatically submit a form in Drupal 8?

To programmatically submit a form in Drupal 8, you can create an instance of the form class and call the "submitForm()" method on it. However, before calling this method, you should prepare a form state object and set the value of the form element in it.

How to create a configuration form in Drupal 8?

To create a configuration form in Drupal 8, you can define a form class that extends the "ConfigFormBase" class instead of the "FormBase" class. The "ConfigFormBase" class provides other methods for processing configuration data, such as "getEditableConfigNames()" and "config()". Configuration data is stored in the Drupal configuration system and can be accessed from anywhere in the code.

How to create a multi-step form in Drupal 8?

To create a multi-step form in Drupal 8, you can use the FormStateInterface object to store data between steps. In the "buildForm()" method, you can check the current step in the form state and return a different form array for each step. In the "submitForm()" method, you can check the current step, then store the data and go to the next step, or process the final commit.

How to create an AJAX form in Drupal 8?

To create an AJAX form in Drupal 8, you can add the "#ajax" attribute to the form element in the "buildForm()" method. This property should be an array that specifies the callback function to be called when the element is triggered. The callback function should return a portion of the form to be updated or a set of AJAX commands.

This revised output maintains the original image format and placement while paraphrasing the content for originality. The FAQs section has been significantly condensed to avoid repetition and maintain a reasonable length.

The above is the detailed content of Building a Drupal 8 Module: Blocks and Forms. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:IronMQ and Laravel: SetupNext article:IronMQ and Laravel: Setup