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

Comments

joachim created an issue. See original summary.

joachim’s picture

Status: Active » Needs review
StatusFileSize
new3.25 KB

Here'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.

Status: Needs review » Needs work
aleksip’s picture

By 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.

borisson_’s picture

@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.

heddn’s picture

What'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...