1

The current system

I have a set of services classes that extends the same base class ex:

class NodeHandler extends ContentEntityHandlerBase {}
class ParagraphHandler extends ContentEntityHandlerBase {}

and this base class receives these dependencies:

class ContentEntityHandlerBase extends ControllerBase {

 public function __construct(EntityTypeManager $entityTypeManager, RequestStack $requestStack, PermissionsService $permissionsService, LoggerChannelFactoryInterface $loggerChannelFactory) {
    $this->entityTypeManager = $entityTypeManager;
    $this->request = $requestStack->getCurrentRequest();
    $this->permissionsService = $permissionsService;
    $this->loggerChannelFactory = $loggerChannelFactory;
  }

So for each child class ex:(NodeHandler , ParagraphHandler) I need always to inject the parent class dependencies ex:

services:
  sacd_rest_api.node_handler:
    class: 'Drupal\sacd_rest_api\service\NodeHandler'
    arguments: ['@entity_type.manager','@request_stack','@sacd_user.permissions','@logger.factory']
  sacd_rest_api.parapgraph_handler:
    class: 'Drupal\sacd_rest_api\service\ParapgrapHandler'
    arguments: ['@entity_type.manager','@request_stack','@sacd_user.permissions','@logger.factory']  

The problem

I don't want to re-inject the parent dependencies each time I create a service class that extends it unless I want to inject a new dependencies that is not inherited from the Base Class for ex:

services:
  sacd_rest_api.node_handler:
    class: 'Drupal\sacd_rest_api\service\NodeHandler'
  sacd_rest_api.parapgraph_handler:
    class: 'Drupal\sacd_rest_api\service\ParapgrapHandler' 

So in order to prevent re-injecting the base class dependencies for all baseClass child service ex: (NodeHandler and ParagraphHandler) so I have use the create() factory method but this doesn't work:

class ContentEntityHandlerBase extends ControllerBase {
 public function __construct(EntityTypeManager $entityTypeManager, RequestStack $requestStack, PermissionsService $permissionsService, LoggerChannelFactoryInterface $loggerChannelFactory) {
    $this->entityTypeManager = $entityTypeManager;
    $this->request = $requestStack->getCurrentRequest();
    $this->permissionsService = $permissionsService;
    $this->loggerChannelFactory = $loggerChannelFactory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('request_stack'),
      $container->get('sacd_user.permissions'),
      $container->get('logger.factory')
    );
  }
}

Question ?

is there any way to ineject the Base Class service , when defining a child class service (NodeHnadler , ParagraphHandler) wihout using the arguments: part in the service definiton ?

1 Answer 1

1

The question asks:

Is there any way to inject the Base Class service , when defining a child class service (NodeHandler , ParagraphHandler) without using the arguments: part in the service definition

Yes. For example, look in core.services.yml to see how this is done in core Drupal. Most core Drupal plugin managers have a parent service named default_plugin_manager. The default_plugin_manager service is defined like this:

  default_plugin_manager:
    abstract: true
    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']

As you can see, there are three services injected into the default_plugin_manager service. Because it is declared as "abstract", this service definition is more like an alias or a placeholder - it's simply there to define a set of arguments. Note there's no class associated with this service.

A concrete plugin manager, for example the plugin.manager.block service, is then defined like this:

  plugin.manager.block:
    class: Drupal\Core\Block\BlockManager
    parent: default_plugin_manager
    arguments: ['@logger.channel.default']

This service has FOUR arguments injected; the three defined by the parent default_plugin_manager plus the addition argument defined here.

When you look at the concrete class BlockManager that implements this service, you see that BlockManager has a constructor that looks like this:

public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LoggerInterface $logger) {
...
}

As you can see, this constructor takes four arguments. The first three defined by the default_plugin_manager service and the last one defined by the plugin.manager.block service.

Core Drupal defines a DefaultPluginManager base class which is extended by BlockManager and other plugins. BlockManager accepts all four arguments in its constructor. Note that if the parent (DefaultPluginManager in this case) has a constructor then you need to call that from the subclass constructor. This way many different plugin managers can extend DefaultPluginManager and inherit those three injected services.

In your specific case, your services.yml file might look like this:

  my_parent_service:
    abstract: true
    arguments: ['@entity_type.manager', '@request_stack', '@sacd_user.permissions', '@logger.factory']

  sacd_rest_api.node_handler:
    class: Drupal\sacd_rest_api\service\NodeHandler
    parent: my_parent_service
  sacd_rest_api.parapgraph_handler:
    class: Drupal\sacd_rest_api\service\ParagraphHandler
    parent: my_parent_service

You do not need a create() method anywhere, and you do not need to use ControllerBase at all.

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.