4

Is there any way to inject container services into FormElement callbacks like '#process'? Or do I need to call \Drupal::container()->get(..)?

I noticed https://www.drupal.org/docs/8/api/services-and-dependency-injection/dependency-injection-for-a-form, but this is for forms, not for form element types.

2
  • 1
    All form element callbacks are simple callables so you would have to pass the whole instantiated object instead of just a class and method to call...and somehow instantiate it(maybe via wrapper?). In the end, it does not make any sense.
    – user21641
    Aug 7, 2017 at 21:25
  • To clarify: I am not so interested whether it is technically called dependency injection, but if there is any clean way to use container services in a #process callback. It seems the answer is no.
    – donquixote
    Aug 8, 2017 at 16:39

2 Answers 2

3

Dependency injection for any method of a class is done for the whole class. So if you have a dependency for the method you're adding for any method in the class, you will set that as a dependency for the whole class, not just a single function. However, #process callbacks are static functions, and static functions cannot use dependency injection. They should use the \Drupal::service() method, which is for static functions and procedural code.

3
  • see comment from @IvanJaros, the class is not instantiated and the callable is a static function, so you have no access to $this.
    – 4uk4
    Aug 8, 2017 at 6:39
  • If the callable is a static function, then there is no dependency injection. Dependency injection is only for non-static methods.
    – Jaypan
    Aug 8, 2017 at 6:40
  • the downvote was not from me, the answer is not wrong. If you add the comment to the answer, it is clearer, that you mean, no, you can't use dependency injection for the static #process callback.
    – 4uk4
    Aug 8, 2017 at 6:52
1

Technically it's possible with the help of ContainerFactoryPluginInterface. The #process is just an array of callables, and it's initialized in getInfo() which is non-static. So, you have access to FormElement's $this when creating the array of #process callbacks. Documentation doesn't seem to put any restrictions on #process array, except requiring it to be an array of callables (nothing is said about them to be static). Also, it's not enforced by any interface nor class inherited by FormElement. So, generally, the following is possible:

<?php

namespace Drupal\my_module\Element;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element\FormElement;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @FormElement("my_element")
 */
class MyElement extends FormElement implements ContainerFactoryPluginInterface {
    /**
     * @var MyDependency
     */
    protected $myDependency;

    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
        return new static(
            $configuration,
            $plugin_id,
            $plugin_definition,
            $container->get('my_dependency')
        );
    }

    public function __construct(array $configuration, $plugin_id, $plugin_definition, MyDependency $my_dependency) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);
        $this->myDependency = $my_dependency;
    }

    public function getInfo() {
        return [
            '#process' => [[$this, 'processMyElement']],
        ];
    }

    public function processMyElement(&$element, FormStateInterface $form_state, &$form) {
        // $this->myDependency available here...
        return $element;
    }
}

Unfortunately I haven't found any instance of non-static #process method in Drupal's core, so maybe there is a convention, or silent agreement, to not use instance methods as callbacks.

3
  • The process element function will be called statically if referenced from outside the class, and will thrown an error. So to make code more bulletrpoof, #process callbacks should be created/treated as static even when in a class like this.
    – Jaypan
    Jun 18, 2022 at 16:05
  • Well, I don't believe anyone developed a code which enforces static invocation of ready to use (static or non-static) callables provided via #process array. No matter, from inside or outside of the class. It's kinda useless. :) Jun 19, 2022 at 11:21
  • There is a risk that this stuff could be serialized, although I think it can be avoided depending how it is used.
    – donquixote
    Jul 7, 2022 at 12:05

Your Answer

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

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