Still to do:
- IndexAddFieldsForm
- IndexStatusForm
- ContentEntity
- ContentAccess
- SearchApiMoreLikeThis
- SearchApiCache
- SearchApiRelationship
Part of this will be covered by #2753763: Add a logging trait which can also log exceptions.
Our plugins currently don't fully utilize dependency injection. Especially, although we purposedly use the t() method for translation, we fail to use dependency injection to get the translation service and thus still use \Drupal::service('string_translation') under the hood. Other services used by individual plugins were also not properly (or at all) injected.
The patter that makes most sense, I think, is this:
public static function create() {
$plugin = parent::create();
$plugin->setService($container->get('service_id'));
return $plugin;
}
public function getService() {
return $this->service ?: \Drupal::service('service_id');
}
public function setService(Service $service) {
$this->service = $service;
}
That way, we also have the advantage of only needing the code for injecting the string translation service once.
Already fixed it, just wanted to have a reference for the commit.
| Comment | File | Size | Author |
|---|---|---|---|
| #13 | 2311039-13--dependency_injection.patch | 74.74 KB | drunken monkey |
Comments
Comment #1
drunken monkeyOK, there are a lot of problems here (not only in plugins, but also forms, and one in the datasource plugin manager), so I didn't manage to fix all of them right now. I'll just do that when I get to the code cleanup of the respective files.
A good rule of thumb is probably that no class which has DI available should contain the string
\Drupal::. One thing I still need to figure out, though, is whether that also applies to\Drupal::formBuilder()in plugin forms. Does it make sense to inject the form builder?Core is useless as a reference here, as it still uses the (deprecated)
form_set_error()function, which is definitely wrong.Comment #3
drunken monkeywatchdog()calls should probably also be replaced with calls to a dependency-injected logger (factory).watchdog_exception()could be a little trickier, as it also has some logic attached, but could be done as well.Comment #4
drunken monkeyComment #6
drunken monkeyComment #7
drunken monkeyStill to do:
- IndexAddFieldsForm
- IndexStatusForm
- ContentEntity
- ContentAccess
- SearchApiMoreLikeThis
- SearchApiCache
- SearchApiRelationship
(Comes from
git grep '\Drupal::' | grep -v 'return \$this->.* ?:' | grep -v '^[^:]*Test').Part of this will be covered by #2753763: Add a logging trait which can also log exceptions.
Comment #8
joachim commentedWhy do you need this:
If you've set the service in create() then it's there. At least that's how every other plugin I've worked with so far works. Is SearchAPI doing something different with the plugin manager?
Comment #9
drunken monkeyIt is done!
Does anyone want to review?
Comment #10
drunken monkeyThis also takes care of the overlap with #2776659: Remove calls to deprecated Utility methods – i.e., provides dependency injection for the instances where
Utility::x()calls would actually need dependency injection.But come to think of it, if we don't need
create()for dependency injection anyways, there's not really a reason to not provide it on entity classes, too, right? Like, just move the\Drupal::service()calls (and similar) to dedicated getters with accompanying property and setter.Any opinions on that?
Comment #12
joachim commentedI seem to remember reading somewhere that the reason entity classes can't have service properties is that the entity object it sometimes serialized.
Comment #13
drunken monkey58 fails for one missing
usestatement?Comment #14
borisson_I don't really like the setter-injection and prefer constructor-only injection myself but overall this patch looks great, let's go ahead and commit this.
Comment #16
drunken monkeyThanks for reviewing, good to hear!
Committed.
Comment #18
ambient.impactThis is a bit of a tangent, but this got me thinking:
While developing some process plug-ins myself, I noticed this difference from core. I've been using constructor-only for so long, and it generally keeps things fairly simple, but sometimes you end up with a really unwieldy mess whenever a sub-class has to inject an additional dependency, like in this example from a plug-in unrelated to Search API:
...which feels a bit comical just to inject one dependency. In this case, setter-injection seems like it would be better as it doesn't require the sub-class to be updated if the ancestor changes its
__construct()parameters. What are your thoughts on this?