Drupal, a powerful content management system, can sometimes feel a bit bloated. But fear not, fellow developers, for we have a few tricks up our sleeves to streamline your code and make it as lean as a racehorse.
1. Autowiring Elegance: Injecting Dependencies Gracefully
Since Drupal 9.3, services can be autowired which means you no longer need to pass arguments via the services.yml file, instead you can specify autowire: true and then utilize you constructor to automatically define the services.
Combined with PHP's latest abilities of defining property visibility, this can reduce the need for property definition, a create() method and needless doc code for IDE's. For example:
Before, we'd have my_module.services.yml with:
yaml
services:
my_module.my_service:
class: Drupal\my_moduke\VitalAdsService
arguments: ['@entity_type.manager', '@config.factory', '@cache.default']And a corresponding service looking like:
php
class MyService {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected ConfigFactoryInterface $configFactory;
/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected CacheBackendInterface $cacheBackend;
/**
* MyService constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The cache backend.
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager, ConfigFactoryInterface $configFactory, CacheBackendInterface $cacheBackend) {
$this->entityTypeManager = $entityTypeManager;
$this->configFactory = $configFactory;
$this->cacheBackend = $cacheBackend;
}But now, we're slimmer than a New Year's Resolution in the first week of January:
yaml
services:
my_module.my_service:
class: Drupal\my_moduke\VitalAdsService
autowire: trueNOTE! you can also set this as the default for all services, see here
And now our class can be so fresh and so clean:
php
class MyService {
/**
* MyService constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The cache backend.
*/
public function __construct(
protected readonly EntityTypeManagerInterface $entityTypeManager,
protected readonly ConfigFactoryInterface $configFactory,
protected readonly CacheBackendInterface $cacheBackend,
) {}2. The AutowireTrait: A Handy Helper for Non-Services
If you're not working with a service but still want to leverage autowiring, the AutowireTrait is your go-to solution. However, remember to ensure that the method signature of the injected dependency matches the one in the trait (for example, plugins have a different signature than controllers and forms).
php
/**
* {@inheritdoc}
*/
public function __construct(
protected readonly EntityTypeManagerInterface $entityTypeManager,
#[Autowire(service: 'commerce_store.current_store')]
protected readonly CurrentStoreInterface $currentStore,
protected readonly AccountProxyInterface $currentUser,
) {}Not the above attribute, this is due to the service not being able to be determine the appropriate service from the interface, in these cases you need to tell autowire which service to typehint.
3. The Constructor Conundrum: A More Subtle Approach
Sometimes, the constructor can feel a bit restrictive. In such cases, consider adding dependencies to the instance in the create method. This allows for more flexibility and can help keep your code clean. Take for example a plugin, in which the parent constuctor will most likely have some dependencies.
php
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->entityTypeManager = $container->get('entity_type.manager');
return $instance;
}