On this page
Dependency Injection for a Form
This documentation needs review. See "Help improve this page" in the sidebar.
Forms that require a Drupal service or a custom service should access the service using dependency injection.
An example form (similar to the form used in Form API in Drupal 8) uses the 'current_user' service to get the uid of the current user. 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;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Implements an example form.
*/
class ExampleForm extends FormBase {
/**
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* @param \Drupal\Core\Session\AccountInterface $account
*/
public function __construct(AccountInterface $account) {
$this->account = $account;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
// Instantiates this form class.
return new static(
// Load the service required to construct this class.
$container->get('current_user')
);
}
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'example_form';
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get current user data.
$uid = $this->account->id();
// ...
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// ...
}
}
The create
method is a factory method that returns a new instance of the ExampleForm
object. The ExampleForm::create
loads one or more services. This can be any core service, defined in core.services.yml
or any *.services.yml
file.
ExampleForm::__construct
uses the services that are loaded by ExampleForm::create
and stores them in properties of the class. The order in which the services are loaded in ExampleForm::create
must be equal to the order of the parameters in the ExampleForm::__construct
method.
The create
method is part of the Drupal\Core\DependencyInjection\ContainerInjectionInterface
which allows controllers to be instantiated with a service. Drupal\Core\Form\FormBase
already implements this interface. Any form that extends Drupal\Core\Form\FormBase
, such as ConfigFormBase and ConfirmFormBase, has this ability of Dependency Injection.
<?php
namespace Drupal\example\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountProxyInterface;
/**
* Implements an example configuration form.
*/
class ExampleConfigForm extends ConfigFormBase {
/**
* Drupal\Core\Session\AccountProxyInterface definition.
*
* @var AccountProxyInterface $currentUser
*/
protected $currentUser;
/**
* Class constructor.
*/
public function __construct(ConfigFactoryInterface $config_factory, AccountProxyInterface $current_user) {
parent::__construct($config_factory);
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'example.config_form',
];
}
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'example_config_form';
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get current user data.
$uid = $this->currentUser->id();
drupal_set_message($uid);
// ...
$config = $this->config('example.config_form');
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('example.config_form')->save();
parent::submitForm($form, $form_state);
}
}
Above example using constructor property promotion (PHP 8.0):
…
class ExampleConfigForm extends ConfigFormBase {
/**
* Class constructor.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
protected AccountProxyInterface $currentUser
) {
parent::__construct($config_factory);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('current_user')
);
}
…
@todo Show how FormController can automatically insert $route info into your buildForm() method by simply defining a parameter with the same class interface.
Example
buildForm(array $form, FormStateInterface $form_state, Request $request = NULL)
Dependency Injection Inside Block (Plugin)
We can inject our custom/core service inside our custom block plugin for that we have to use:
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
Suppose you have a custom block with the name 'drupalise' then you inject your custom service 'drupalise' inside your custom block code as follows:
<?php
namespace Drupal\drupalise\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\drupalise\Services\DrupaliseMe;
/**
* Provides a 'Drupalise' block.
*
* @Block(
* id = "drupalise",
* admin_label = @Translation("Drupalise"),
* )
*/
class Drupalise extends BlockBase implements ContainerFactoryPluginInterface {
/**
* @var $drupalise \Drupal\drupalise\Services\DrupaliseMe
*/
protected $drupalise;
/**
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* @param array $configuration
* @param string $plugin_id
* @param mixed $plugin_definition
*
* @return static
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('drupalise')
);
}
/**
* @param array $configuration
* @param string $plugin_id
* @param mixed $plugin_definition
* @param \Drupal\drupalise\Services\DrupaliseMe $drupalise_me
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, DrupaliseMe $drupalise_me) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->drupalise = $drupalise_me;
}
/**
* {@inheritdoc}
*/
public function build() {
$slogan= $this->drupalise->Drupalise(); // Function from your service file will return 'Drop Drop Drupal !!!'
$build = [];
$build['drupalise']['#markup'] = 'Drupalise is best' . $slogan;
return $build;
}
}
Considerations for Serialization
When service objects are seralized by drupal private properties are lost.
Be sure to not set any service properties in your form class to have private visibility.
For example, when a form is rebuilt via ajax as in when you add an #ajax
definition to a form element, the form class is serialized on
ajax requests and any services that are assigned to private properties in the class are lost. This results in a form where the ajax runs and proceses successfully then generates errors due to null property values on subsequent ajax requests.
When you have an element with a defined #ajax
handler like the below, you'll need to ensure the services injected into your form class
are not private.
'#ajax' => [
'callback' => '::myAjaxCallback', 'event' => 'change', 'wrapper' => 'my-ajax-wrapper', 'progress' => [
'type' => 'throbber', 'message' => $this->t('Verifying entry...'),
],
]
Example:
class MyEntityForm extends EntityForm
/**
* Drupal routeBuilder object, for rebuilding routes on form save.
*
* This is an injected service and must not be declared private or
* it will be lost on serialisation of this form object.
*
* @var \Drupal\Core\Routing\RouteBuilder
*/
protected $routeBuilder
/**
* MyEntityForm constructor.
*
* The injected service properties, eg $this->routeBuilder must
* be not be declared private as this form is reloaded with ajax and
* cached, private properties aren't serialisable.
*
* @param \Drupal\Core\Routing\RouteBuilderInterface $routeBuilder
*/
public function __construct(RouteBuilderInterface $routeBuilder) {
$this->routeBuilder = $routeBuilder
}
}
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion