Phần 2 viết Drupal 8 Module: Blocks and Forms

Phần 2 viết Drupal 8 Module: Blocks and Forms

In the first installment of this article series on Drupal 8 module development we started with the basics. We’ve seen what files were needed to let Drupal know about our module, how the routing process works and how to create menu links programatically as configuration.

In this tutorial we are going to go a bit further with our sandbox module found in this repository and look at two new important pieces of functionality: blocks and forms. To this end, we will create a custom block that returns some configurable text. After that, we will create a simple form used to print out user submitted values to the screen.

Drupal 8 blocks

A cool new change to the block API in D8 has been a switch to making blocks more prominent, by making them plugins (a brand new concept). What this means is that they are reusable pieces of functionality (under the hood) as you can now create a block in the UI and reuse it across the site – you are no longer limited to using a block only one time.

Let’s go ahead and create a simple block type that prints to the screen Hello World! by default. All we need to work with is one class file located in the src/Plugin/Blockfolder of our module’s root directory. Let’s call our new block type DemoBlock, and naturally it needs to reside in a file called DemoBlock.php. Inside this file, we can start with the following:

<?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');
  } 
}

Like with all other class files we start by namespacing our class. Then we use the BlockBaseclass so that we can extend it, as well as the AccountInterfaceclass so that we can get access to the currently logged in user. Then follows something you definitely have not seen in Drupal 7: annotations.

Annotations are a PHP discovery tool located in the comment block of the same file as the class definition. Using these annotations we let Drupal know that we want to register a new block type (@Block) with the id of demo_blockand the admin_labelof Demo block (passed through the translation system).

Next, we extend the BlockBaseclass into our own DemoBlock, inside of which we implement two methods (the most common ones you’ll implement). The build()method is the most important as it returns a renderable array the block will print out. The access()method controls access rights for viewing this block. The parameter passed to it is an instance of the AccountInterfaceclass which will be in this case the current user.

Another interesting thing to note is that we are no longer using the t()function globally for translation but we reference the t()method implemented in the class parent.

And that’s it, you can clear the caches and go to the Block layoutconfiguration page. The cool thing is that you have the block types on the right (that you can filter through) and you can place one or more blocks of those types to various regions on the site.

Drupal 8 block configuration

Now that we’ve seen how to create a new block type to use from the UI, let’s tap further into the API and add a configuration form for it. We will make it so that you can edit the block, specify a name in a textfield and then the block will say hello to that name rather than the world.

First, we’ll need to define the form that contains our textfield. So inside our DemoBlockclass we can add a new method called blockForm():

/**

 * {@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;

}

This form API implementation should look very familiar from Drupal 7. There are, however, some new things going on here. First, we retrieve the $formarray from the parent class (so we are building on the existing form by adding our own field). Standard OOP stuff. Then, we retrieve and store the configuration for this block. The BlockBaseclass defines the getConfiguration()method that does this for us. And we place the demo_block_settingsvalue as the #default_valuein case it has been set already.

Next, it’s time for the submit handler of this form that will process the value of our field and store it in the block’s configuration:

/**

* {@inheritdoc}

*/

public function blockSubmit($form, &$form_state) { 

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

} 

This method also goes inside the DemoBlockclass and all it does is save the value of the demo_block_settingsfield as a new item in the block’s configuration (keyed by the same name for consistency).

Lastly, we need to adapt our build()method to include the name to say hello to:

/**

 * {@inheritdoc}

 */

public function build() {

  

  $config = $this->getConfiguration();

  

  if (isset($config['demo_block_settings']) && !empty($config['demo_block_settings'])) {

    $name = $config['demo_block_settings'];

  }

  else {

    $name = $this->t('to no one');

  }

  

  return array(

    '#markup' => $this->t('Hello @name!', array('@name' => $name)),

  );  

}

By now, this should look fairly easy. We are retrieving the block’s configuration and if the value of our field is set, we use it for the printed statement. If not, use use a generic one. You can clear the cache and test it out by editing the block you assigned to a region and add a name to say hello to. One thing to keep in mind is that you are still responsible for sanitizing user input upon printing to the screen. I have not included these steps for brevity.

Drupal 8 forms

The last thing we are going to explore in this tutorial is how to create a simple form. Due to space limitations, I will not cover the configuration management aspect of it (storing configuration values submitted through forms). Rather, I will illustrate a simple form definition, the values submitted being simply printed on the screen to show that it works.

In Drupal 8, form definition functions are all grouped together inside a class. So let’s define our simple DemoFormclass inside src/Form/DemoForm.php:

<?php


/**

 * @file

 * Contains \Drupal\demo\Form\DemoForm.

 */


namespace Drupal\demo\Form;


use Drupal\Core\Form\FormBase;


class DemoForm extends FormBase {

  

  /**

   * {@inheritdoc}.

   */

  public function getFormId() {

    return 'demo_form';

  }

  

  /**

   * {@inheritdoc}.

   */

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

    

    $form['email'] = array(

      '#type' => 'email',

      '#title' => $this->t('Your .com email address.')

    );

    $form['show'] = array(

      '#type' => 'submit',

      '#value' => $this->t('Submit'),

    );

    

    return $form;

  }

  

  /**

   * {@inheritdoc}

   */

  public function validateForm(array &$form, array &$form_state) {

    

    if (strpos($form_state['values']['email'], '.com') === FALSE ) {

      $this->setFormError('email', $form_state, $this->t('This is not a .com email address.'));

    }

  }

  

  /**

   * {@inheritdoc}

   */

  public function submitForm(array &$form, array &$form_state) {

    

    drupal_set_message($this->t('Your email address is @email', array('@email' => $form_state['values']['email'])));

  }

  

}

Apart from the OOP side of it, everything should look very familiar to Drupal 7. The Form API has remained pretty much unchanged (except for the addition of some new form elements and this class encapsulation). So what happens above?

First, we namespace the class and use the core FormBaseclass so we can extend it with our own DemoFormclass. Then we implement 4 methods, 3 of which should look very familiar. The getFormId()method is new and mandatory, used simply to return the machine name of the form. The buildForm()method is again mandatory and it builds up the form. How? Just like you are used to from Drupal 7. The validateForm()method is optional and its purpose should also be quite clear from D7. And finally, the submitForm()method does the submission handling. Very logical and organised.

So what are we trying to achieve with this form? We have an email field (a new form element in Drupal 8) we want users to fill out. By default, Drupal checks whether the value input is in fact an email address. But in our validation function we make sure it is a .comemail address and if not, we set a form error on the field. Lastly, the submit handler just prints a message on the page.

One last thing we need to do in order to use this form is provide a route for it. So edit the demo.routing.ymlfile and add the following:

demo.form:

  path: '/demo/form'

  defaults:

    _form: '\Drupal\demo\Form\DemoForm'

    _title: 'Demo Form'

  requirements:

    _permission: 'access content'

This should look familiar from the previous article in which we routed a simple page. The only big difference is that instead of _contentunder defaults, we use _formto specify that the target is a form class. And the value is therefore the class name we just created.

Clear the caches and navigate to demo/formto see the form and test it out.

If you are familiar with drupal_get_form()and are wondering how to load a form like we used to in Drupal 7, the answer is in the global Drupal class. Thus to retrieve a form, you can use its formBuilder()method and do something like this:

$form = \Drupal::formBuilder()->getForm('Drupal\demo\Form\DemoForm');

Then you can return $formwhich will be the renderable array of the form.

Conclusion

In this article we’ve continued our exploration of Drupal 8 module development with two new topics: blocks and forms. We’ve seen how to create our own block type we can use to create blocks in the UI. We’ve also learned how to add a custom configuration to it and store the values for later use. On the topic of forms, we’ve seen a simple implementation of the FormBaseclass that we used to print out to the screen the value submitted by the user.

In the next tutorial we will take a quick look at configuration forms. We will save the values submitted by the user using the Drupal 8 configuration system. Additionally, we will look at the service container and dependency injection and how those work in Drupal 8. See you then.

 

Bạn thấy bài viết này như thế nào?: 
No votes yet
Ảnh của Khanh Hoang

Khanh Hoang - Kenn

Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.

Tìm kiếm bất động sản

 

Advertisement

 

jobsora

Dich vu khu trung tphcm

Dich vu diet chuot tphcm

Dich vu diet con trung

Quảng Cáo Bài Viết

 

Blocks trong Drupal 7 bây giờ là plugins trong Drupal 8

In Drupal 7, definition and altering of blocks are done through block specific mechanisms. A block was defined by implementing providing the available deltas and the corresponding administrative labels

Adding a New Drupal Block Position

Thêm 1 vị trí Drupal Block với ShallowGrunge.Module

One of our students wanted to put a block position inside the red header bar. If you haven't done so already, download and install ShallowGrunge: http://drupal.org/project/shallowgrunge

7 điều thú vị từ DrupalCon Sydney

7 điều thú vị từ DrupalCon Sydney

At the beginning of February, Australia held the first ever DrupalCon outside of Europe or North America.

Công ty diệt chuột T&C

 

Diet con trung