SearchAPI plugins have a weird pattern for their injected services.
The __construct() method is not overridden, but instead create() calls setters on the returned object.
Eg in RenderedItem:
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
/** @var static $plugin */
$plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$plugin->setCurrentUser($container->get('current_user'));
$plugin->setRenderer($container->get('renderer'));
$plugin->setLogger($container->get('logger.channel.search_api'));
$plugin->setThemeManager($container->get('theme.manager'));
$plugin->setThemeInitializer($container->get('theme.initialization'));
$plugin->setConfigFactory($container->get('config.factory'));
return $plugin;
}
This then requires tons of thin setters like this:
public function setCurrentUser(AccountProxyInterface $current_user) {
$this->currentUser = $current_user;
return $this;
}
and for reasons which escape me, there are getters that guard against the service being set:
public function getCurrentUser() {
return $this->currentUser ?: \Drupal::currentUser();
}
Several observations:
- The plugins all implement SearchAPI's ConfigurablePluginInterface, which itself inherits from core's ContainerFactoryPluginInterface.
- That means that the plugin manager will *always* instantiate a plugin by calling create() and passing in the $container. So the guarded getters are not necessary
- It would be simpler, and also in line with core's pattern for DI plugins, to get the services from $container in create(), and pass them in to an overridden __create(). There is then no need for setter methods.
Benefits of this would be:
- much less boilerplate code
- follow same pattern as core plugin types
- better DX
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | 2945833-2.search_api.implement-standard-di-pattern-plugins.patch | 3.25 KB | joachim |
Comments
Comment #2
joachim commentedHere's an example of the change I mean.
Quick patch to update just one plugin. I made this mostly with Module Builder -- created a module called searchapi, then created a plugin with the ID of one that already exists and set the services that it uses. I then removed the setters & getters and changed the calls to them manually.
Comment #4
aleksipBy using setters and getters there is no breakage if the parent constructor changes. For an example case see https://www.drupal.org/node/2856625
Not familiar with Search API code but it looks like the guards are also to reduce dependency on the parent.
Comment #5
borisson_@joachim: I've had many a discussion about this with @drunken monkey. I agree, he doesn't.
I think, at this point, this would create a huge API break and unneeded amount of breaks in other patches that we shouldn't do this in the 8.1 branch of Search API. If/when we open 8.2, we could do this, but now this doesn't seem like good plan.
Comment #6
heddnWhat's being done here is actually a pretty nifty way to shield onself from any BC breaks in the parent. See https://www.previousnext.com.au/blog/safely-extending-drupal-8-plugin-cl...