1

How can I change form builder call in class in to dependency injection method?

My current code:

class CustomBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $form = \Drupal::formBuilder()->getForm('Drupal\custom_module\Form\CustomForm');
    return $form;
  }

}

And I tried:

class CustomBlock extends BlockBase {

  /**
   * formBuilder.   *
   */
  protected $formBuilder;

  /**
   * Class constructor.
   */
  public function __construct($formBuilder) {
    $this->formBuilder = $formBuilder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
        $container->get('form_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $form = $this->formBuilder->getForm('Drupal\custom_module\Form\CustomForm');
    return $form;
  }

}

But getting following error:

Fatal error: Call to a member function getForm() on a non-object in H:\wamp\www\myproject\modules\custom\custom_module\src\Plugin\Block\CustomBlock.php on line 44

4 Answers 4

3

You have to implement the \Drupal\Core\Plugin\ContainerFactoryPluginInterface. The \Drupal\Core\Block\BlockBase doesn't implement it by default.

Also, your IDE is probably not that good since it would show you the create() method is not implemented.

1
  • 2
    The constructor is also missing the type hint.
    – Kevin
    Jul 30, 2017 at 12:42
3

AJAX form test mock module's AjaxFormBlock.php is quite a nice example for getting both in one, the form builder and the form interface! Look how it implements FormInterface, ContainerFactoryPluginInterface to then provide the form from within the block and injecting the form builder via dependency injection to then be used to get the form in the block's build() method at the same time.

Of course, when you only need the form builder, you don't need to implement the FormInterface! Then you just need the ContainerFactoryPluginInterface.

namespace Drupal\ajax_forms_test\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides an AJAX form block.
 *
 * @Block(
 *   id = "ajax_forms_test_block",
 *   admin_label = @Translation("AJAX test form"),
 *   category = @Translation("Forms")
 * )
 */
class AjaxFormBlock extends BlockBase implements FormInterface, ContainerFactoryPluginInterface {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * Constructs a new AjaxFormBlock.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin ID for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder, MessengerInterface $messenger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->formBuilder = $form_builder;
    $this->messenger = $messenger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('form_builder'),
      $container->get('messenger')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    return $this->formBuilder->getForm($this);
  }

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['test1'] = [
      '#type' => 'select',
      '#title' => $this->t('Test 1'),
      '#required' => TRUE,
      '#options' => [
        'option1' => $this->t('Option 1'),
        'option2' => $this->t('Option 2'),
      ],
      '#ajax' => [
        'callback' => '::updateOptions',
        'wrapper' => 'edit-test1-wrapper',
      ],
      '#prefix' => '<div id="edit-test1-wrapper">',
      '#suffix' => '</div>',
    ];
    $form['actions'] = [
      '#type' => 'actions',
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];
    return $form;
  }

  /**
   * Updates the options of a select list.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return array
   *   The updated form element.
   */
  public function updateOptions(array $form, FormStateInterface $form_state) {
    $form['test1']['#options']['option1'] = $this->t('Option 1!!!');
    $form['test1']['#options'] += [
      'option3' => $this->t('Option 3'),
      'option4' => $this->t('Option 4'),
    ];
    return $form['test1'];
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->messenger->addStatus('Submission successful.');
  }

}

Another nice example is core's SearchBlock.php.

Also worth checking out the docs on Dependency injection in Plugin (Block).

1

Try this this solution will work.

<?php

namespace Drupal\firstcustommodule\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a 'Hello' block.
 *
 * @Block(
 *   id = "hello_block",
 *   admin_label = @Translation("Hello Block")
 * )
 */
class HelloBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The form builder service.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * Constructs a HelloBlock object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin ID for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->formBuilder = $form_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    // Instantiate this block class.
    return new static($configuration, $plugin_id, $plugin_definition,
      // Load the service required to construct this class.
      $container->get('form_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    return $this->formBuilder->getForm('Drupal\firstcustommodule\Form\CompanyAddForm');
  }

}
-2

Simple Answer with updated code you just forgot to Import namespace at top and you have not defined the FormBuilderInterface type of object @ constructor

 use Drupal\Core\Form\FormBuilderInterface;
 class CustomBlock extends BlockBase {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
   protected $formBuilder;

  /**
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder The Form Builder.
   */

  public function __construct(FormBuilderInterface $formBuilder) {
    $this->formBuilder = $formBuilder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
        $container->get('form_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $form = $this->formBuilder->getForm('Drupal\custom_module\Form\CustomForm');
    return $form;
  }

}
2
  • 1
    To use a namespace unfortunately is no dependency injection.
    – leymannx
    Sep 24, 2019 at 12:03
  • 1
    The usestatement declares which interfaces/classes/traits are used, but that doesn't inject dependencies.
    – apaderno
    Oct 11, 2019 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.