SlideShare a Scribd company logo
1 of 81
Download to read offline
Drupal 8 Services
And
Dependency Injection.
Phil Norton
Phil Norton
Technical Lead at
Help run NWDUG
Blog at #! code (www.hashbangcode.com)
@philipnorton42 on Twitter
NWDUGNORTH WEST DRUPAL USER GROUP
2ND TUESDAY / MONTH
STREAM ON YOUTUBE
3RD UNCONFERENCE IN
NOVEMBER
FOLLOW @NWDUG
Drupal::service('thing');
What Are Drupal
Services?
ā€¢ Allow access to lots of things in Drupal 8.
ā€¢ Wrap objects and deļ¬ne a common interface.
ā€¢ Automatic Dependency Injection.
ā€¢ Powerful
ā€¢ Certiļ¬ed awesome
conļ¬g
cron
renderer
path
route
ļ¬le_system
plugin
cache
date
access_manager
event_dispatcher
menu
translation
entity
Usage.
$object = Drupal::service('thing');
$object->method();
$pathManager = Drupal::service('path.alias_manager');
$path = 'somepath';
$normalPath = $pathManager->getPathByAlias($path);
$pathManager = Drupal::service('path.alias_manager');
$path = 'somepath';
$normalPath = $pathManager->getPathByAlias($path);
Instantiated AliasManager object
$normalPath = Drupal::service('path.alias_manager')
->getPathByAlias('somepath');
/* @var DrupalCorePathAliasManager $pathManager */
$pathManager = Drupal::service('path.alias_manager');
Where To Find
Services.
Services are deļ¬ned in yml ļ¬les.
<module>.services.yml
core.services.yml
path.alias_whitelist:
class: DrupalCorePathAliasWhitelist
tags:
- { name: needs_destruction }
arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state',
'@path.alias_storage']
path.alias_manager:
class: DrupalCorePathAliasManager
arguments: ['@path.alias_storage', '@path.alias_whitelist', '@language_manager',
'@cache.data']
path.current:
class: DrupalCorePathCurrentPathStack
arguments: ['@request_stack']
/**ā€Ø
* The default alias manager implementation.ā€Ø
*/ā€Ø
class AliasManager implements AliasManagerInterface, CacheDecoratorInterface
{ā€Ø
ā€Ø
Ā /**ā€Ø
Ā Ā * Constructs an AliasManager.ā€Ø
Ā Ā *ā€Ø
Ā Ā * @param DrupalCorePathAliasStorageInterface $storageā€Ø
Ā Ā * Ā Ā The alias storage service.ā€Ø
Ā Ā * @param DrupalCorePathAliasWhitelistInterface $whitelistā€Ø
Ā Ā * Ā Ā The whitelist implementation to use.ā€Ø
Ā Ā * @param DrupalCoreLanguageLanguageManagerInterface $language_managerā€Ø
Ā Ā * Ā Ā The language manager.ā€Ø
Ā Ā * @param DrupalCoreCacheCacheBackendInterface $cacheā€Ø
Ā Ā * Ā Ā Cache backend.ā€Ø
Ā Ā */ā€Ø
Ā public function __construct(AliasStorageInterface $storage,
AliasWhitelistInterface $whitelist, LanguageManagerInterface
$language_manager, CacheBackendInterface $cache) {ā€Ø
Ā Ā Ā $this->storage = $storage;ā€Ø
Ā Ā Ā $this->languageManager = $language_manager;ā€Ø
Ā Ā Ā $this->whitelist = $whitelist;ā€Ø
Ā Ā Ā $this->cache = $cache;ā€Ø
Ā }
path.alias_manager:
class:
DrupalCorePathAliasManager
arguments: [ā€Ø
'@path.alias_storage',
ā€Ø
'@path.alias_whitelist', ā€Ø
ā€Ø
'@language_manager', ā€Ø
ā€Ø
'@cache.data'ā€Ø
ā€Ø
]
public function __construct(
AliasStorageInterface $storage,
AliasWhitelistInterface $whitelist,
LanguageManagerInterface
$language_manager,
CacheBackendInterface $cache
) {
Service Argument
Types.
'@conļ¬g.factory' == Another service.
'%container.modules%' == A conļ¬guration item.
'conļ¬g' or true == A variable.
/**ā€Ø
* The default alias manager implementation.ā€Ø
*/ā€Ø
class AliasManager implements AliasManagerInterface,
CacheDecoratorInterface {ā€Ø
ā€Ø
Ā /**ā€Ø
Ā Ā * Constructs an AliasManager.ā€Ø
Ā Ā *ā€Ø
Ā Ā * @param DrupalCorePathAliasStorageInterface $storageā€Ø
Ā Ā * Ā Ā The alias storage service.ā€Ø
Ā Ā * @param DrupalCorePathAliasWhitelistInterface $whitelistā€Ø
Ā Ā * Ā Ā The whitelist implementation to use.ā€Ø
Ā Ā * @param DrupalCoreLanguageLanguageManagerInterface
$language_managerā€Ø
Ā Ā * Ā Ā The language manager.ā€Ø
Ā Ā * @param DrupalCoreCacheCacheBackendInterface $cacheā€Ø
Ā Ā * Ā Ā Cache backend.ā€Ø
Ā Ā */ā€Ø
Ā public function __construct(AliasStorageInterface $storage,
AliasWhitelistInterface $whitelist, LanguageManagerInterface
$language_manager, CacheBackendInterface $cache) {ā€Ø
Ā Ā Ā $this->storage = $storage;ā€Ø
Ā Ā Ā $this->languageManager = $language_manager;ā€Ø
Ā Ā Ā $this->whitelist = $whitelist;ā€Ø
Ā Ā Ā $this->cache = $cache;ā€Ø
Ā }
Dependency
Injection.
Dependency Injection.
ā€¢ Instantiate the object you want without having to
worry about dependencies.
ā€¢ Symfony DependencyInjection Component
manages these dependencies.
path.alias_managerā€Ø
Without Using
Dependency Injection.
use DrupalCorePathAliasManager;
$pathManager = new AliasManager($storage, $whitelist,
$language_manager, $cache);
use DrupalCorePathAliasManager;
use DrupalCoreAliasStorage;
$storage = new AliasStorage($database, $moduleHandler);
$pathManager = new AliasManager($storage, $whitelist,
$languageManager, $cache);
use DrupalCorePathAliasManager;
use DrupalCoreAliasStorage;
use DrupalCoreDatabaseDatabase;
use DrupalCoreExtensionModuleHandler;
$database = Database::getConnection();
$moduleHandler = new ModuleHandler($root, $moduleList,
$cacheBackend);
$storage = new AliasStorage($database, $moduleHandler);
$pathManager = new AliasManager($storage, $whitelist,
$languageManager, $cache);
use DrupalCorePathAliasManager;
use DrupalCoreAliasStorage;
use DrupalCoreDatabaseDatabase;
use DrupalCoreExtensionModuleHandler;
use DrupalCoreCacheCacheBackendInterface;
use DrupalCoreAppRootFactory;
use DrupalCoreCacheDatabaseBackend;
$database = Database::getConnection();
$root = new AppRootFactory($drupalKernel);
$moduleList = Drupal::getContainer()
->getParameter('container.modules');
$cacheBackend = new DatabaseBackend($connection,
$checksumProvider, $bin);
$moduleHandler = new ModuleHandler($root, $moduleList,
$cacheBackend);
$storage = new AliasStorage($database, $moduleHandler);
$pathManager = new AliasManager($storage, $whitelist,
$languageManager, $cache);
use DrupalCorePathAliasManager;
use DrupalCoreAliasStorage;
use DrupalCoreDatabaseDatabase;
use DrupalCoreExtensionModuleHandler;
use DrupalCoreCacheCacheBackendInterface;
use DrupalCoreAppRootFactory;
use DrupalCoreCacheDatabaseBackend;
$database = Database::getConnection();
$drupalKernel = ???
$connection = $database;
$checksumProvider = ???
$bin = ???
$root = new AppRootFactory($drupalKernel);
$moduleList = Drupal::getContainer()
->getParameter('container.modules');
$cacheBackend = new DatabaseBackend($connection,
$checksumProvider, $bin);
$moduleHandler = new ModuleHandler($root, $moduleList,
$cacheBackend);
$storage = new AliasStorage($database, $moduleHandler);
$pathManager = new AliasManager($storage, $whitelist,
$languageManager, $cache);
$pathManager = Drupal::service('path.alias_manager');
Dependency Injection
Interface.
ā€¢ Controllers and Forms implement the interface:
DrupalCoreDependencyInjectionā€Ø
ContainerInjectionInterface
ā€¢ Which means they can be injected with Drupal
services.
ā€¢ A static create() method deļ¬nes the services you
need in the controller or form.
class ExampleController extends ControllerBase {
protected $configFactory;
protected $pathAliasManager;
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('path.alias_manager')
);
}
public function __construct(ConfigFactoryInterface
$configFactory, AliasManager $pathAliasManager) {
$this->configFactory = $configFactory;
$this->pathAliasManager = $pathAliasManager;
}
}
public function someAction() {
...
$normalPath = Drupal::service('path.alias_manager')
->getPathByAlias('somepath');
...
}
public function someAction() {
...
$normalPath = Drupal::service('path.alias_manager')
->getPathByAlias('somepath');
$normalPath = $this->pathAliasManager
->getPathByAlias('somepath');
...
}
Quick Recap.
ā€¢ Services and their dependencies are deļ¬ned in
*.services.yml ļ¬les.
ā€¢ Dependency injection makes life easier.
ā€¢ Controllers and Forms can have dependencies
injected into them.
ā€¢ So how do we build our own?
Create Your Own.
<module>.services.yml
Create a services
deļ¬nition ļ¬le.
<module>.services.yml
services:
<service name>:
class: Drupal<full namespace of class>
arguments: ['@config.factory', ...]
Service Argument
Types.
'@conļ¬g.factory' == Another service.
'%container.modules%' == A conļ¬guration item.
'conļ¬g' or true == A variable.
my_module.services.yml
services:
my_module.myservice:
class: Drupalmy_moduleServiceNameModuleService
arguments: ['@config.factory']
Create Service Interface.
namespace Drupalmy_moduleServiceName;
use DrupalCoreConfigConfigFactoryInterface;
interface ModuleServiceInterface {
public function doSomething();
}
Create Service Class.
namespace Drupalmy_moduleServiceName;
use DrupalCoreConfigConfigFactoryInterface;
class ModuleService implements ModuleServiceInterface {
protected $moduleConfigThing;
public function __construct(ConfigFactoryInterface
$config_factory) {
$this->moduleConfigThing = $config_factory
->get('module.config')
->get('config_thing');
}
public function doSomething() {
}
}
Start Using It.
Drupal::service('my_module.myservice')->doSomething();
PCA Predict.
A real example of Drupal services in action
PCA Predict Module.
ā€¢ Used for address matching and auto complete in
forms.
ā€¢ Integrate with PCA Predict web services.
ā€¢ Drupal service created to wrap the web service.
pcapredict.services.yml
services:
pcapredict:
class: Drupalpcapredict_integrationPcaPredictPcaPredict
arguments: ['@config.factory']
namespace Drupalpcapredict_integrationPcaPredict;
use DrupalCoreConfigConfigFactoryInterface;
use Drupalpcapredict_integrationPcaPredictPcaPredictInterface;
interface PcaPredictInterface {
public function find(array $values, $type = ā€˜json');
public function retrieve(array $values, $type = ā€˜json');
}
namespace Drupalpcapredict_integrationPcaPredict;
use DrupalCoreConfigConfigFactoryInterface;
use Drupalpcapredict_integrationPcaPredictPcaPredictInterface;
class PcaPredict implements PcaPredictInterface {
protected $pcaPredictKey;
public function __construct(ConfigFactoryInterface
$config_factory) {
$this->pcaPredictKey = $config_factory
->get('pcapredict.settings')
->get('pcapredict_apikey');
}
public function find(array $values, $type = 'json') {
// Load data from the API.
}
public function retrieve(array $values, $type = 'json') {
// Load data from the API.
}
}
namespace Drupalpcapredict_integrationPcaPredict;
use DrupalCoreConfigConfigFactoryInterface;
use Drupalpcapredict_integrationPcaPredictPcaPredictInterface;
class PcaPredict implements PcaPredictInterface {
protected $pcaPredictKey;
public function __construct(ConfigFactoryInterface
$config_factory) {
$this->pcaPredictKey = $config_factory
->get('pcapredict.settings')
->get('pcapredict_apikey');
}
public function find(array $values, $type = 'json') {
// Load data from the API.
}
public function retrieve(array $values, $type = 'json') {
// Load data from the API.
}
}
Implements our
interface
Store the
API key
in a
property
$results = Drupal::service('pcapredict')
->find(['postcode' => 'M32 0RS']);
Altering Services.
Altering Services.
ā€¢ All services can be overridden or altered.
ā€¢ A service provider class allows this.
ā€¢ These are automatically detected by Drupal.
ā€¢ Camel case version of the module name. Sufļ¬xed
by the words ā€œServiceProviderā€ in the src directory.
ā€¢ Some examples of this in action.
Problem:
Shield module prevents
testing of API service on
staging website.
Solution:
Poke a hole in the shield!
Module shield_override.
ā€¢ Convert module name to camel case.
ā€¢ shield_override becomes ShieldOverride.
ā€¢ Sufļ¬xed by the words ā€œServiceProviderā€.ā€Ø
ā€Ø
ShieldOverrideServiceProvider
Module shield_override.
shield_override.info.yml
src/ShieldOverrideServiceProvider.php
src/ShieldOverride.php
namespace Drupalshield_override;
use DrupalCoreDependencyInjectionContainerBuilder;
use DrupalCoreDependencyInjectionServiceProviderBase;
class ShieldOverrideServiceProvider extends ServiceProviderBase {
public function alter(ContainerBuilder $container) {
// Decorate the shield module to prevent it from
// triggering on certain routes.
$definition = $container->getDefinition('shield.middleware');
$definition->setClass('Drupalshield_overrideShieldOverride');
}
}
ShieldOverrideServiceProvider.php
namespace Drupalshield_override;
use DrupalshieldShieldMiddleware;
use SymfonyComponentHttpFoundationRequest;
class ShieldOverride extends ShieldMiddleware {
public function handle(Request $request, $type =
self::MASTER_REQUEST, $catch = TRUE) {
// Get the current request URI.
$currentPath = $request->getRequestUri();
// Get the current method (e.g. GET or POST).
$currentMethod = $request->getMethod();
if (($currentMethod == 'POST' || $currentMethod == 'GET')
&& strstr($currentPath, '/the/soap/service/path') !== FALSE) {
// If we are attempting to access the soap service via
// a POST or GET HTTP method then handle the request
// without invoking the Shield module.
return $this->httpKernel->handle($request, $type, $catch);
}
// Always handle the request using the default
// Shield behaviour.
return parent::handle($request, $type, $catch);
}
}
namespace Drupalshield_override;
use DrupalshieldShieldMiddleware;
use SymfonyComponentHttpFoundationRequest;
class ShieldOverride extends ShieldMiddleware {
public function handle(Request $request, $type =
self::MASTER_REQUEST, $catch = TRUE) {
// Get the current request URI.
$currentPath = $request->getRequestUri();
// Get the current method (e.g. GET or POST).
$currentMethod = $request->getMethod();
if (($currentMethod == 'POST' || $currentMethod == 'GET')
&& strstr($currentPath, '/the/soap/service/path') !== FALSE) {
// If we are attempting to access the soap service via
// a POST or GET HTTP method then handle the request
// without invoking the Shield module.
return $this->httpKernel->handle($request, $type, $catch);
}
// Always handle the request using the default
// Shield behaviour.
return parent::handle($request, $type, $catch);
}
}
Extends the original
Shield module class
Runs the original
Shield code.
Problem:
Testing PCA Predict needs an
API account and costs money
per transaction.
Solution:
Create a ā€˜stubā€™ service override
that doesnā€™t use the API.
Module pcapredict_stub.
ā€¢ Convert module name to camel case.
ā€¢ pcapredict_stub becomes PcapredictStub.
ā€¢ Sufļ¬xed by the words ā€œServiceProviderā€.ā€Ø
ā€Ø
PcapredictStubServiceProvider
PCA Predict Stub
Module.
pcapredict_stub.info.yml
src/PcapredictStubServiceProvider.php
src/PcaPredict/PcaPredictStub.php
namespace Drupalpcapredict_stub;
use DrupalCoreDependencyInjectionContainerBuilder;
use DrupalCoreDependencyInjectionServiceProviderBase;
class PcapredictStubServiceProvider extends ServiceProviderBase {
public function alter(ContainerBuilder $container) {
// Override the PcaPredict class with a new class.
$definition = $container->getDefinition('pcapredict');
$definition->setClass('Drupalpcapredict_stubPcaPredictPcaPredictStub');
}
}
PcapredictStubServiceProvider.php
namespace Drupalpcapredict_stubPcaPredict;
use DrupalpcapredictPcaPredictPcaPredictInterface;
use DrupalCoreConfigConfigFactoryInterface;
class PcaPredictStub implements PcaPredictInterface {
protected $pcaPredictKey;
public function __construct(ConfigFactoryInterface $config_factory) {
$this->pcaPredictKey = $config_factory
->get('pcapredict.settings')
->get('pcapredict_apikey');
}
public function find(array $values, $type = 'json') {
// Load data from a CSV file.
}
public function retrieve(array $values, $type = 'json') {
// Load data from a CSV file.
}
}
namespace Drupalpcapredict_stubPcaPredict;
use DrupalpcapredictPcaPredictPcaPredictInterface;
use DrupalCoreConfigConfigFactoryInterface;
class PcaPredictStub implements PcaPredictInterface {
protected $pcaPredictKey;
public function __construct(ConfigFactoryInterface $config_factory) {
$this->pcaPredictKey = $config_factory
->get('pcapredict.settings')
->get('pcapredict_apikey');
}
public function find(array $values, $type = 'json') {
// Load data from a CSV file.
}
public function retrieve(array $values, $type = 'json') {
// Load data from a CSV file.
}
}
Implements
PcaPredictInterface
ļ¬nd() /
retrieve()API
Address
ļ¬nder
ļ¬nd() /
retrieve()
Address
ļ¬nder
Stub Modules.
ā€¢ Stub modules are useful for:
ā€¢ Prevent analytics being sent.
ā€¢ Bypass complex setups / ļ¬rewalls.
ā€¢ Testing without using the API.
Problem:
Filtering a AJAX View with
Group context results in
access error.
Solution:
Create our own access rule.
Group View.
ā€¢ Group module used to manage members.
ā€¢ Created a View to show all group members and
some information about them.
ā€¢ Added AJAX ļ¬lters to allow the user list to be
ļ¬ltered.
ā€¢ Group context was loaded into the View using a
contextual ļ¬lter and the path: ā€Ø
/account/%group/members
https://www.example.com/account/123/members
https://www.example.com/account/123/members
https://www.example.com/views/ajax?_wrapper_format=drupal_ajax
(+ POST data)
namespace DrupalgroupContext;
use DrupalCoreCacheCacheableMetadata;
use DrupalCorePluginContextContext;
use DrupalCorePluginContextContextDefinition;
use DrupalCorePluginContextContextProviderInterface;
use DrupalCoreRoutingRouteMatchInterface;
use DrupalCoreStringTranslationStringTranslationTrait;
/**
Ā * Sets the current group as a context on group routes.
Ā */
class GroupRouteContext implements ContextProviderInterface {
use GroupRouteContextTrait;
// ... snip ...
/**
Ā Ā Ā * {@inheritdoc}
Ā Ā Ā */
public function getRuntimeContexts(array $unqualified_context_ids) {
// Create an optional context definition for group entities.
$context_definition = new ContextDefinition('entity:group', NULL, FALSE);
// Cache this context on the route.
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
// Create a context from the definition and retrieved or created group.
$context = new Context($context_definition, $this->getGroupFromRoute());
$context->addCacheableDependency($cacheability);
return ['group' => $context];
}
// ... snip ...
}
namespace DrupalgroupContext;
use DrupalCoreCacheCacheableMetadata;
use DrupalCorePluginContextContext;
use DrupalCorePluginContextContextDefinition;
use DrupalCorePluginContextContextProviderInterface;
use DrupalCoreRoutingRouteMatchInterface;
use DrupalCoreStringTranslationStringTranslationTrait;
/**
Ā * Sets the current group as a context on group routes.
Ā */
class GroupRouteContext implements ContextProviderInterface {
use GroupRouteContextTrait;
// ... snip ...
/**
Ā Ā Ā * {@inheritdoc}
Ā Ā Ā */
public function getRuntimeContexts(array $unqualified_context_ids) {
// Create an optional context definition for group entities.
$context_definition = new ContextDefinition('entity:group', NULL, FALSE);
// Cache this context on the route.
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
// Create a context from the definition and retrieved or created group.
$context = new Context($context_definition, $this->getGroupFromRoute());
$context->addCacheableDependency($cacheability);
return ['group' => $context];
}
// ... snip ...
}
No Group in route
when in AJAX mode
group.services.yml
group.group_route_context:ā€Ø
class: 'DrupalgroupContextGroupRouteContext'ā€Ø
arguments: ['@current_route_match']ā€Ø
tags:ā€Ø
- { name: 'context_provider' }
GroupViewsServiceProvider.php
namespace Drupalgroup_views;
use DrupalCoreDependencyInjectionContainerBuilder;
use DrupalCoreDependencyInjectionServiceProviderBase;
class GroupViewsServiceProvider extends ServiceProviderBase {
public function alter(ContainerBuilder $container) {
// Decorate the group_route_context service to inject our own objects on
// certain routes.
$definition = $container->getDefinition('group.group_route_context');
$definition->setClass('Drupalgroup_subscriptionsGroupViewsAccessOverride');
}
}
namespace Drupalgroup_views;
use DrupalgroupContextGroupRouteContext;
use DrupalCorePluginContextContextDefinition;
use DrupalCoreCacheCacheableMetadata;
use DrupalCorePluginContextContext;
class GroupViewsAccessOverride extends GroupRouteContext {
public function getRuntimeContexts(array $unqualified_context_ids) {
$request = Drupal::request();
// Get the current request URI.
$currentPathInfo = $request->getPathInfo();
// Get the current method (e.g. GET or POST).
$currentMethod = $request->getMethod();
// Extract the parameters out of the post arguments.
parse_str($request->getContent(), $postArgs);
if ($currentMethod == 'POST'
&& $currentPathInfo == '/views/ajax'
&& isset($postArgs['view_name'])
&& isset($postArgs['view_args'])
&& isset($postArgs['_drupal_ajax'])
&& $postArgs['view_name'] == 'group_members'
&& is_numeric($postArgs['view_args'])
&& $postArgs['_drupal_ajax'] == '1'
) {
// This is our view.
$context_definition = new ContextDefinition('entity:group', NULL, FALSE);
// Cache this context on the route.
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
// Create a context from the definition and retrieved or created group.
$groupEntity = Drupal::service('entity_type.manager')->getStorage('group')->load($postArgs['view_args']);
if ($groupEntity) {
// We have loaded a group.
$context = new Context($context_definition, $groupEntity);
$context->addCacheableDependency($cacheability);
return ['group' => $context];
}
}
// Always handle the request using the default GroupRouteContext behaviour.
return parent::getRuntimeContexts($unqualified_context_ids);
}
}
namespace Drupalgroup_views;
use DrupalgroupContextGroupRouteContext;
use DrupalCorePluginContextContextDefinition;
use DrupalCoreCacheCacheableMetadata;
use DrupalCorePluginContextContext;
class GroupViewsAccessOverride extends GroupRouteContext {
public function getRuntimeContexts(array $unqualified_context_ids) {
$request = Drupal::request();
// Get the current request URI.
$currentPathInfo = $request->getPathInfo();
// Get the current method (e.g. GET or POST).
$currentMethod = $request->getMethod();
// Extract the parameters out of the post arguments.
parse_str($request->getContent(), $postArgs);
if ($currentMethod == 'POST'
&& $currentPathInfo == '/views/ajax'
&& isset($postArgs['view_name'])
&& isset($postArgs['view_args'])
&& isset($postArgs['_drupal_ajax'])
&& $postArgs['view_name'] == 'group_members'
&& is_numeric($postArgs['view_args'])
&& $postArgs['_drupal_ajax'] == '1'
) {
// This is our view.
$context_definition = new ContextDefinition('entity:group', NULL, FALSE);
// Cache this context on the route.
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
// Create a context from the definition and retrieved or created group.
$groupEntity = Drupal::service('entity_type.manager')->getStorage('group')->load($postArgs['view_args']);
if ($groupEntity) {
// We have loaded a group.
$context = new Context($context_definition, $groupEntity);
$context->addCacheableDependency($cacheability);
return ['group' => $context];
}
}
// Always handle the request using the default GroupRouteContext behaviour.
return parent::getRuntimeContexts($unqualified_context_ids);
}
}
Extends the original
Group module class
Checking to ensure
that this is the
correct context.
Continue on as
normal.
Load Group object
and add it to a
context.
Resources.
ā€¢ Services and Dependency Injection Containerā€Ø
https://api.drupal.org/api/drupal/core!core.api.php/
group/container/
ā€¢ List of all servicesā€Ø
https://api.drupal.org/api/drupal/services
ā€¢ Services And Dependency Injectionā€Ø
https://www.drupal.org/docs/8/api/services-and-
dependency-injection
NWDUGNORTH WEST DRUPAL USER GROUP
2ND TUESDAY / MONTH
STREAM ON YOUTUBE
3RD UNCONFERENCE IN
NOVEMBER
FOLLOW @NWDUG
Thank you.
Phil Norton
Technical Lead at
Help run NWDUG
Blog at #! code (www.hashbangcode.com)
@philipnorton42 on Twitter

More Related Content

Similar to Drupal 8 Services And Dependency Injection

Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data ModelAttila Jenei
Ā 
Drupal 8 Services
Drupal 8 ServicesDrupal 8 Services
Drupal 8 ServicesPhilip Norton
Ā 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
Ā 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)arcware
Ā 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Fabien Potencier
Ā 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
Ā 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
Ā 
Php on the desktop and php gtk2
Php on the desktop and php gtk2Php on the desktop and php gtk2
Php on the desktop and php gtk2Elizabeth Smith
Ā 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?Alexandru Badiu
Ā 
Migrating to dependency injection
Migrating to dependency injectionMigrating to dependency injection
Migrating to dependency injectionJosh Adell
Ā 
関č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄
関č„æPHP勉強会 php5.4ć¤ć¾ćæćć„é–¢č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄
関č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄Hisateru Tanaka
Ā 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperGary Hockin
Ā 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf Conference
Ā 
Drupal 8 simple page: Mi primer proyecto en Drupal 8.
Drupal 8 simple page: Mi primer proyecto en Drupal 8.Drupal 8 simple page: Mi primer proyecto en Drupal 8.
Drupal 8 simple page: Mi primer proyecto en Drupal 8.Samuel SolĆ­s Fuentes
Ā 
Drupal 8: Entities
Drupal 8: EntitiesDrupal 8: Entities
Drupal 8: Entitiesdrubb
Ā 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
Ā 
Unittests fĆ¼r Dummies
Unittests fĆ¼r DummiesUnittests fĆ¼r Dummies
Unittests fĆ¼r DummiesLars Jankowfsky
Ā 
Magento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowMagento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowVrann Tulika
Ā 

Similar to Drupal 8 Services And Dependency Injection (20)

Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data Model
Ā 
Drupal 8 Services
Drupal 8 ServicesDrupal 8 Services
Drupal 8 Services
Ā 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Ā 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
Ā 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Ā 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Ā 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
Ā 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
Ā 
Php on the desktop and php gtk2
Php on the desktop and php gtk2Php on the desktop and php gtk2
Php on the desktop and php gtk2
Ā 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?
Ā 
Migrating to dependency injection
Migrating to dependency injectionMigrating to dependency injection
Migrating to dependency injection
Ā 
関č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄
関č„æPHP勉強会 php5.4ć¤ć¾ćæćć„é–¢č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄
関č„æPHP勉強会 php5.4ć¤ć¾ćæ恐恄
Ā 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
Ā 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
Ā 
Drupal 8 simple page: Mi primer proyecto en Drupal 8.
Drupal 8 simple page: Mi primer proyecto en Drupal 8.Drupal 8 simple page: Mi primer proyecto en Drupal 8.
Drupal 8 simple page: Mi primer proyecto en Drupal 8.
Ā 
Drupal 8: Entities
Drupal 8: EntitiesDrupal 8: Entities
Drupal 8: Entities
Ā 
Fatc
FatcFatc
Fatc
Ā 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
Ā 
Unittests fĆ¼r Dummies
Unittests fĆ¼r DummiesUnittests fĆ¼r Dummies
Unittests fĆ¼r Dummies
Ā 
Magento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowMagento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request Flow
Ā 

More from Philip Norton

Getting Into Drupal 8 Configuration
Getting Into Drupal 8 ConfigurationGetting Into Drupal 8 Configuration
Getting Into Drupal 8 ConfigurationPhilip Norton
Ā 
Webform and Drupal 8
Webform and Drupal 8Webform and Drupal 8
Webform and Drupal 8Philip Norton
Ā 
Acquia Drupal Certification
Acquia Drupal CertificationAcquia Drupal Certification
Acquia Drupal CertificationPhilip Norton
Ā 
Becoming A Drupal Master Builder
Becoming A Drupal Master BuilderBecoming A Drupal Master Builder
Becoming A Drupal Master BuilderPhilip Norton
Ā 
Drupal Performance : DrupalCamp North
Drupal Performance : DrupalCamp NorthDrupal Performance : DrupalCamp North
Drupal Performance : DrupalCamp NorthPhilip Norton
Ā 
Drupal 8 Configuration Management
Drupal 8 Configuration ManagementDrupal 8 Configuration Management
Drupal 8 Configuration ManagementPhilip Norton
Ā 
Getting Started With Jenkins And Drupal
Getting Started With Jenkins And DrupalGetting Started With Jenkins And Drupal
Getting Started With Jenkins And DrupalPhilip Norton
Ā 
Drupal theming
Drupal themingDrupal theming
Drupal themingPhilip Norton
Ā 
Making The Drupal Pill Easier To Swallow
Making The Drupal Pill Easier To SwallowMaking The Drupal Pill Easier To Swallow
Making The Drupal Pill Easier To SwallowPhilip Norton
Ā 
Drupal 7 Queues
Drupal 7 QueuesDrupal 7 Queues
Drupal 7 QueuesPhilip Norton
Ā 

More from Philip Norton (12)

ReactPHP
ReactPHPReactPHP
ReactPHP
Ā 
Getting Into Drupal 8 Configuration
Getting Into Drupal 8 ConfigurationGetting Into Drupal 8 Configuration
Getting Into Drupal 8 Configuration
Ā 
Webform and Drupal 8
Webform and Drupal 8Webform and Drupal 8
Webform and Drupal 8
Ā 
Acquia Drupal Certification
Acquia Drupal CertificationAcquia Drupal Certification
Acquia Drupal Certification
Ā 
Becoming A Drupal Master Builder
Becoming A Drupal Master BuilderBecoming A Drupal Master Builder
Becoming A Drupal Master Builder
Ā 
Drupal Performance : DrupalCamp North
Drupal Performance : DrupalCamp NorthDrupal Performance : DrupalCamp North
Drupal Performance : DrupalCamp North
Ā 
Drupal 8 Configuration Management
Drupal 8 Configuration ManagementDrupal 8 Configuration Management
Drupal 8 Configuration Management
Ā 
Getting Started With Jenkins And Drupal
Getting Started With Jenkins And DrupalGetting Started With Jenkins And Drupal
Getting Started With Jenkins And Drupal
Ā 
Drupal theming
Drupal themingDrupal theming
Drupal theming
Ā 
Drush
DrushDrush
Drush
Ā 
Making The Drupal Pill Easier To Swallow
Making The Drupal Pill Easier To SwallowMaking The Drupal Pill Easier To Swallow
Making The Drupal Pill Easier To Swallow
Ā 
Drupal 7 Queues
Drupal 7 QueuesDrupal 7 Queues
Drupal 7 Queues
Ā 

Recently uploaded

DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamUiPathCommunity
Ā 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
Ā 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfOrbitshub
Ā 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
Ā 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
Ā 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...apidays
Ā 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistandanishmna97
Ā 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
Ā 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vƔzquez
Ā 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
Ā 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
Ā 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
Ā 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
Ā 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
Ā 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
Ā 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Angeliki Cooney
Ā 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
Ā 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
Ā 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
Ā 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
Ā 

Recently uploaded (20)

DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
Ā 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
Ā 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Ā 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Ā 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
Ā 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Ā 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
Ā 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
Ā 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Ā 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
Ā 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Ā 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Ā 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
Ā 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
Ā 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
Ā 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Ā 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
Ā 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
Ā 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
Ā 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
Ā 

Drupal 8 Services And Dependency Injection

  • 1. Drupal 8 Services And Dependency Injection. Phil Norton
  • 2. Phil Norton Technical Lead at Help run NWDUG Blog at #! code (www.hashbangcode.com) @philipnorton42 on Twitter
  • 3. NWDUGNORTH WEST DRUPAL USER GROUP 2ND TUESDAY / MONTH STREAM ON YOUTUBE 3RD UNCONFERENCE IN NOVEMBER FOLLOW @NWDUG
  • 5.
  • 6. What Are Drupal Services? ā€¢ Allow access to lots of things in Drupal 8. ā€¢ Wrap objects and deļ¬ne a common interface. ā€¢ Automatic Dependency Injection. ā€¢ Powerful ā€¢ Certiļ¬ed awesome
  • 10. $pathManager = Drupal::service('path.alias_manager'); $path = 'somepath'; $normalPath = $pathManager->getPathByAlias($path);
  • 11. $pathManager = Drupal::service('path.alias_manager'); $path = 'somepath'; $normalPath = $pathManager->getPathByAlias($path); Instantiated AliasManager object
  • 13. /* @var DrupalCorePathAliasManager $pathManager */ $pathManager = Drupal::service('path.alias_manager');
  • 15. Services are deļ¬ned in yml ļ¬les. <module>.services.yml
  • 16. core.services.yml path.alias_whitelist: class: DrupalCorePathAliasWhitelist tags: - { name: needs_destruction } arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', '@path.alias_storage'] path.alias_manager: class: DrupalCorePathAliasManager arguments: ['@path.alias_storage', '@path.alias_whitelist', '@language_manager', '@cache.data'] path.current: class: DrupalCorePathCurrentPathStack arguments: ['@request_stack']
  • 17.
  • 18. /**ā€Ø * The default alias manager implementation.ā€Ø */ā€Ø class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {ā€Ø ā€Ø Ā /**ā€Ø Ā Ā * Constructs an AliasManager.ā€Ø Ā Ā *ā€Ø Ā Ā * @param DrupalCorePathAliasStorageInterface $storageā€Ø Ā Ā * Ā Ā The alias storage service.ā€Ø Ā Ā * @param DrupalCorePathAliasWhitelistInterface $whitelistā€Ø Ā Ā * Ā Ā The whitelist implementation to use.ā€Ø Ā Ā * @param DrupalCoreLanguageLanguageManagerInterface $language_managerā€Ø Ā Ā * Ā Ā The language manager.ā€Ø Ā Ā * @param DrupalCoreCacheCacheBackendInterface $cacheā€Ø Ā Ā * Ā Ā Cache backend.ā€Ø Ā Ā */ā€Ø Ā public function __construct(AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {ā€Ø Ā Ā Ā $this->storage = $storage;ā€Ø Ā Ā Ā $this->languageManager = $language_manager;ā€Ø Ā Ā Ā $this->whitelist = $whitelist;ā€Ø Ā Ā Ā $this->cache = $cache;ā€Ø Ā }
  • 19. path.alias_manager: class: DrupalCorePathAliasManager arguments: [ā€Ø '@path.alias_storage', ā€Ø '@path.alias_whitelist', ā€Ø ā€Ø '@language_manager', ā€Ø ā€Ø '@cache.data'ā€Ø ā€Ø ] public function __construct( AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache ) {
  • 20. Service Argument Types. '@conļ¬g.factory' == Another service. '%container.modules%' == A conļ¬guration item. 'conļ¬g' or true == A variable.
  • 21. /**ā€Ø * The default alias manager implementation.ā€Ø */ā€Ø class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {ā€Ø ā€Ø Ā /**ā€Ø Ā Ā * Constructs an AliasManager.ā€Ø Ā Ā *ā€Ø Ā Ā * @param DrupalCorePathAliasStorageInterface $storageā€Ø Ā Ā * Ā Ā The alias storage service.ā€Ø Ā Ā * @param DrupalCorePathAliasWhitelistInterface $whitelistā€Ø Ā Ā * Ā Ā The whitelist implementation to use.ā€Ø Ā Ā * @param DrupalCoreLanguageLanguageManagerInterface $language_managerā€Ø Ā Ā * Ā Ā The language manager.ā€Ø Ā Ā * @param DrupalCoreCacheCacheBackendInterface $cacheā€Ø Ā Ā * Ā Ā Cache backend.ā€Ø Ā Ā */ā€Ø Ā public function __construct(AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {ā€Ø Ā Ā Ā $this->storage = $storage;ā€Ø Ā Ā Ā $this->languageManager = $language_manager;ā€Ø Ā Ā Ā $this->whitelist = $whitelist;ā€Ø Ā Ā Ā $this->cache = $cache;ā€Ø Ā }
  • 23. Dependency Injection. ā€¢ Instantiate the object you want without having to worry about dependencies. ā€¢ Symfony DependencyInjection Component manages these dependencies.
  • 25. use DrupalCorePathAliasManager; $pathManager = new AliasManager($storage, $whitelist, $language_manager, $cache);
  • 26. use DrupalCorePathAliasManager; use DrupalCoreAliasStorage; $storage = new AliasStorage($database, $moduleHandler); $pathManager = new AliasManager($storage, $whitelist, $languageManager, $cache);
  • 27. use DrupalCorePathAliasManager; use DrupalCoreAliasStorage; use DrupalCoreDatabaseDatabase; use DrupalCoreExtensionModuleHandler; $database = Database::getConnection(); $moduleHandler = new ModuleHandler($root, $moduleList, $cacheBackend); $storage = new AliasStorage($database, $moduleHandler); $pathManager = new AliasManager($storage, $whitelist, $languageManager, $cache);
  • 28. use DrupalCorePathAliasManager; use DrupalCoreAliasStorage; use DrupalCoreDatabaseDatabase; use DrupalCoreExtensionModuleHandler; use DrupalCoreCacheCacheBackendInterface; use DrupalCoreAppRootFactory; use DrupalCoreCacheDatabaseBackend; $database = Database::getConnection(); $root = new AppRootFactory($drupalKernel); $moduleList = Drupal::getContainer() ->getParameter('container.modules'); $cacheBackend = new DatabaseBackend($connection, $checksumProvider, $bin); $moduleHandler = new ModuleHandler($root, $moduleList, $cacheBackend); $storage = new AliasStorage($database, $moduleHandler); $pathManager = new AliasManager($storage, $whitelist, $languageManager, $cache);
  • 29. use DrupalCorePathAliasManager; use DrupalCoreAliasStorage; use DrupalCoreDatabaseDatabase; use DrupalCoreExtensionModuleHandler; use DrupalCoreCacheCacheBackendInterface; use DrupalCoreAppRootFactory; use DrupalCoreCacheDatabaseBackend; $database = Database::getConnection(); $drupalKernel = ??? $connection = $database; $checksumProvider = ??? $bin = ??? $root = new AppRootFactory($drupalKernel); $moduleList = Drupal::getContainer() ->getParameter('container.modules'); $cacheBackend = new DatabaseBackend($connection, $checksumProvider, $bin); $moduleHandler = new ModuleHandler($root, $moduleList, $cacheBackend); $storage = new AliasStorage($database, $moduleHandler); $pathManager = new AliasManager($storage, $whitelist, $languageManager, $cache);
  • 31. Dependency Injection Interface. ā€¢ Controllers and Forms implement the interface: DrupalCoreDependencyInjectionā€Ø ContainerInjectionInterface ā€¢ Which means they can be injected with Drupal services. ā€¢ A static create() method deļ¬nes the services you need in the controller or form.
  • 32. class ExampleController extends ControllerBase { protected $configFactory; protected $pathAliasManager; public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), $container->get('path.alias_manager') ); } public function __construct(ConfigFactoryInterface $configFactory, AliasManager $pathAliasManager) { $this->configFactory = $configFactory; $this->pathAliasManager = $pathAliasManager; } }
  • 33. public function someAction() { ... $normalPath = Drupal::service('path.alias_manager') ->getPathByAlias('somepath'); ... }
  • 34. public function someAction() { ... $normalPath = Drupal::service('path.alias_manager') ->getPathByAlias('somepath'); $normalPath = $this->pathAliasManager ->getPathByAlias('somepath'); ... }
  • 35. Quick Recap. ā€¢ Services and their dependencies are deļ¬ned in *.services.yml ļ¬les. ā€¢ Dependency injection makes life easier. ā€¢ Controllers and Forms can have dependencies injected into them. ā€¢ So how do we build our own?
  • 38. <module>.services.yml services: <service name>: class: Drupal<full namespace of class> arguments: ['@config.factory', ...]
  • 39. Service Argument Types. '@conļ¬g.factory' == Another service. '%container.modules%' == A conļ¬guration item. 'conļ¬g' or true == A variable.
  • 41. Create Service Interface. namespace Drupalmy_moduleServiceName; use DrupalCoreConfigConfigFactoryInterface; interface ModuleServiceInterface { public function doSomething(); }
  • 42. Create Service Class. namespace Drupalmy_moduleServiceName; use DrupalCoreConfigConfigFactoryInterface; class ModuleService implements ModuleServiceInterface { protected $moduleConfigThing; public function __construct(ConfigFactoryInterface $config_factory) { $this->moduleConfigThing = $config_factory ->get('module.config') ->get('config_thing'); } public function doSomething() { } }
  • 44. PCA Predict. A real example of Drupal services in action
  • 45. PCA Predict Module. ā€¢ Used for address matching and auto complete in forms. ā€¢ Integrate with PCA Predict web services. ā€¢ Drupal service created to wrap the web service.
  • 47. namespace Drupalpcapredict_integrationPcaPredict; use DrupalCoreConfigConfigFactoryInterface; use Drupalpcapredict_integrationPcaPredictPcaPredictInterface; interface PcaPredictInterface { public function find(array $values, $type = ā€˜json'); public function retrieve(array $values, $type = ā€˜json'); }
  • 48. namespace Drupalpcapredict_integrationPcaPredict; use DrupalCoreConfigConfigFactoryInterface; use Drupalpcapredict_integrationPcaPredictPcaPredictInterface; class PcaPredict implements PcaPredictInterface { protected $pcaPredictKey; public function __construct(ConfigFactoryInterface $config_factory) { $this->pcaPredictKey = $config_factory ->get('pcapredict.settings') ->get('pcapredict_apikey'); } public function find(array $values, $type = 'json') { // Load data from the API. } public function retrieve(array $values, $type = 'json') { // Load data from the API. } }
  • 49. namespace Drupalpcapredict_integrationPcaPredict; use DrupalCoreConfigConfigFactoryInterface; use Drupalpcapredict_integrationPcaPredictPcaPredictInterface; class PcaPredict implements PcaPredictInterface { protected $pcaPredictKey; public function __construct(ConfigFactoryInterface $config_factory) { $this->pcaPredictKey = $config_factory ->get('pcapredict.settings') ->get('pcapredict_apikey'); } public function find(array $values, $type = 'json') { // Load data from the API. } public function retrieve(array $values, $type = 'json') { // Load data from the API. } } Implements our interface Store the API key in a property
  • 52. Altering Services. ā€¢ All services can be overridden or altered. ā€¢ A service provider class allows this. ā€¢ These are automatically detected by Drupal. ā€¢ Camel case version of the module name. Sufļ¬xed by the words ā€œServiceProviderā€ in the src directory. ā€¢ Some examples of this in action.
  • 53. Problem: Shield module prevents testing of API service on staging website. Solution: Poke a hole in the shield!
  • 54. Module shield_override. ā€¢ Convert module name to camel case. ā€¢ shield_override becomes ShieldOverride. ā€¢ Sufļ¬xed by the words ā€œServiceProviderā€.ā€Ø ā€Ø ShieldOverrideServiceProvider
  • 56. namespace Drupalshield_override; use DrupalCoreDependencyInjectionContainerBuilder; use DrupalCoreDependencyInjectionServiceProviderBase; class ShieldOverrideServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { // Decorate the shield module to prevent it from // triggering on certain routes. $definition = $container->getDefinition('shield.middleware'); $definition->setClass('Drupalshield_overrideShieldOverride'); } } ShieldOverrideServiceProvider.php
  • 57. namespace Drupalshield_override; use DrupalshieldShieldMiddleware; use SymfonyComponentHttpFoundationRequest; class ShieldOverride extends ShieldMiddleware { public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { // Get the current request URI. $currentPath = $request->getRequestUri(); // Get the current method (e.g. GET or POST). $currentMethod = $request->getMethod(); if (($currentMethod == 'POST' || $currentMethod == 'GET') && strstr($currentPath, '/the/soap/service/path') !== FALSE) { // If we are attempting to access the soap service via // a POST or GET HTTP method then handle the request // without invoking the Shield module. return $this->httpKernel->handle($request, $type, $catch); } // Always handle the request using the default // Shield behaviour. return parent::handle($request, $type, $catch); } }
  • 58. namespace Drupalshield_override; use DrupalshieldShieldMiddleware; use SymfonyComponentHttpFoundationRequest; class ShieldOverride extends ShieldMiddleware { public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { // Get the current request URI. $currentPath = $request->getRequestUri(); // Get the current method (e.g. GET or POST). $currentMethod = $request->getMethod(); if (($currentMethod == 'POST' || $currentMethod == 'GET') && strstr($currentPath, '/the/soap/service/path') !== FALSE) { // If we are attempting to access the soap service via // a POST or GET HTTP method then handle the request // without invoking the Shield module. return $this->httpKernel->handle($request, $type, $catch); } // Always handle the request using the default // Shield behaviour. return parent::handle($request, $type, $catch); } } Extends the original Shield module class Runs the original Shield code.
  • 59. Problem: Testing PCA Predict needs an API account and costs money per transaction. Solution: Create a ā€˜stubā€™ service override that doesnā€™t use the API.
  • 60. Module pcapredict_stub. ā€¢ Convert module name to camel case. ā€¢ pcapredict_stub becomes PcapredictStub. ā€¢ Sufļ¬xed by the words ā€œServiceProviderā€.ā€Ø ā€Ø PcapredictStubServiceProvider
  • 62. namespace Drupalpcapredict_stub; use DrupalCoreDependencyInjectionContainerBuilder; use DrupalCoreDependencyInjectionServiceProviderBase; class PcapredictStubServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { // Override the PcaPredict class with a new class. $definition = $container->getDefinition('pcapredict'); $definition->setClass('Drupalpcapredict_stubPcaPredictPcaPredictStub'); } } PcapredictStubServiceProvider.php
  • 63. namespace Drupalpcapredict_stubPcaPredict; use DrupalpcapredictPcaPredictPcaPredictInterface; use DrupalCoreConfigConfigFactoryInterface; class PcaPredictStub implements PcaPredictInterface { protected $pcaPredictKey; public function __construct(ConfigFactoryInterface $config_factory) { $this->pcaPredictKey = $config_factory ->get('pcapredict.settings') ->get('pcapredict_apikey'); } public function find(array $values, $type = 'json') { // Load data from a CSV file. } public function retrieve(array $values, $type = 'json') { // Load data from a CSV file. } }
  • 64. namespace Drupalpcapredict_stubPcaPredict; use DrupalpcapredictPcaPredictPcaPredictInterface; use DrupalCoreConfigConfigFactoryInterface; class PcaPredictStub implements PcaPredictInterface { protected $pcaPredictKey; public function __construct(ConfigFactoryInterface $config_factory) { $this->pcaPredictKey = $config_factory ->get('pcapredict.settings') ->get('pcapredict_apikey'); } public function find(array $values, $type = 'json') { // Load data from a CSV file. } public function retrieve(array $values, $type = 'json') { // Load data from a CSV file. } } Implements PcaPredictInterface
  • 67. Stub Modules. ā€¢ Stub modules are useful for: ā€¢ Prevent analytics being sent. ā€¢ Bypass complex setups / ļ¬rewalls. ā€¢ Testing without using the API.
  • 68. Problem: Filtering a AJAX View with Group context results in access error. Solution: Create our own access rule.
  • 69. Group View. ā€¢ Group module used to manage members. ā€¢ Created a View to show all group members and some information about them. ā€¢ Added AJAX ļ¬lters to allow the user list to be ļ¬ltered. ā€¢ Group context was loaded into the View using a contextual ļ¬lter and the path: ā€Ø /account/%group/members
  • 72. namespace DrupalgroupContext; use DrupalCoreCacheCacheableMetadata; use DrupalCorePluginContextContext; use DrupalCorePluginContextContextDefinition; use DrupalCorePluginContextContextProviderInterface; use DrupalCoreRoutingRouteMatchInterface; use DrupalCoreStringTranslationStringTranslationTrait; /** Ā * Sets the current group as a context on group routes. Ā */ class GroupRouteContext implements ContextProviderInterface { use GroupRouteContextTrait; // ... snip ... /** Ā Ā Ā * {@inheritdoc} Ā Ā Ā */ public function getRuntimeContexts(array $unqualified_context_ids) { // Create an optional context definition for group entities. $context_definition = new ContextDefinition('entity:group', NULL, FALSE); // Cache this context on the route. $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); // Create a context from the definition and retrieved or created group. $context = new Context($context_definition, $this->getGroupFromRoute()); $context->addCacheableDependency($cacheability); return ['group' => $context]; } // ... snip ... }
  • 73. namespace DrupalgroupContext; use DrupalCoreCacheCacheableMetadata; use DrupalCorePluginContextContext; use DrupalCorePluginContextContextDefinition; use DrupalCorePluginContextContextProviderInterface; use DrupalCoreRoutingRouteMatchInterface; use DrupalCoreStringTranslationStringTranslationTrait; /** Ā * Sets the current group as a context on group routes. Ā */ class GroupRouteContext implements ContextProviderInterface { use GroupRouteContextTrait; // ... snip ... /** Ā Ā Ā * {@inheritdoc} Ā Ā Ā */ public function getRuntimeContexts(array $unqualified_context_ids) { // Create an optional context definition for group entities. $context_definition = new ContextDefinition('entity:group', NULL, FALSE); // Cache this context on the route. $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); // Create a context from the definition and retrieved or created group. $context = new Context($context_definition, $this->getGroupFromRoute()); $context->addCacheableDependency($cacheability); return ['group' => $context]; } // ... snip ... } No Group in route when in AJAX mode
  • 75. GroupViewsServiceProvider.php namespace Drupalgroup_views; use DrupalCoreDependencyInjectionContainerBuilder; use DrupalCoreDependencyInjectionServiceProviderBase; class GroupViewsServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { // Decorate the group_route_context service to inject our own objects on // certain routes. $definition = $container->getDefinition('group.group_route_context'); $definition->setClass('Drupalgroup_subscriptionsGroupViewsAccessOverride'); } }
  • 76. namespace Drupalgroup_views; use DrupalgroupContextGroupRouteContext; use DrupalCorePluginContextContextDefinition; use DrupalCoreCacheCacheableMetadata; use DrupalCorePluginContextContext; class GroupViewsAccessOverride extends GroupRouteContext { public function getRuntimeContexts(array $unqualified_context_ids) { $request = Drupal::request(); // Get the current request URI. $currentPathInfo = $request->getPathInfo(); // Get the current method (e.g. GET or POST). $currentMethod = $request->getMethod(); // Extract the parameters out of the post arguments. parse_str($request->getContent(), $postArgs); if ($currentMethod == 'POST' && $currentPathInfo == '/views/ajax' && isset($postArgs['view_name']) && isset($postArgs['view_args']) && isset($postArgs['_drupal_ajax']) && $postArgs['view_name'] == 'group_members' && is_numeric($postArgs['view_args']) && $postArgs['_drupal_ajax'] == '1' ) { // This is our view. $context_definition = new ContextDefinition('entity:group', NULL, FALSE); // Cache this context on the route. $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); // Create a context from the definition and retrieved or created group. $groupEntity = Drupal::service('entity_type.manager')->getStorage('group')->load($postArgs['view_args']); if ($groupEntity) { // We have loaded a group. $context = new Context($context_definition, $groupEntity); $context->addCacheableDependency($cacheability); return ['group' => $context]; } } // Always handle the request using the default GroupRouteContext behaviour. return parent::getRuntimeContexts($unqualified_context_ids); } }
  • 77. namespace Drupalgroup_views; use DrupalgroupContextGroupRouteContext; use DrupalCorePluginContextContextDefinition; use DrupalCoreCacheCacheableMetadata; use DrupalCorePluginContextContext; class GroupViewsAccessOverride extends GroupRouteContext { public function getRuntimeContexts(array $unqualified_context_ids) { $request = Drupal::request(); // Get the current request URI. $currentPathInfo = $request->getPathInfo(); // Get the current method (e.g. GET or POST). $currentMethod = $request->getMethod(); // Extract the parameters out of the post arguments. parse_str($request->getContent(), $postArgs); if ($currentMethod == 'POST' && $currentPathInfo == '/views/ajax' && isset($postArgs['view_name']) && isset($postArgs['view_args']) && isset($postArgs['_drupal_ajax']) && $postArgs['view_name'] == 'group_members' && is_numeric($postArgs['view_args']) && $postArgs['_drupal_ajax'] == '1' ) { // This is our view. $context_definition = new ContextDefinition('entity:group', NULL, FALSE); // Cache this context on the route. $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); // Create a context from the definition and retrieved or created group. $groupEntity = Drupal::service('entity_type.manager')->getStorage('group')->load($postArgs['view_args']); if ($groupEntity) { // We have loaded a group. $context = new Context($context_definition, $groupEntity); $context->addCacheableDependency($cacheability); return ['group' => $context]; } } // Always handle the request using the default GroupRouteContext behaviour. return parent::getRuntimeContexts($unqualified_context_ids); } } Extends the original Group module class Checking to ensure that this is the correct context. Continue on as normal. Load Group object and add it to a context.
  • 78. Resources. ā€¢ Services and Dependency Injection Containerā€Ø https://api.drupal.org/api/drupal/core!core.api.php/ group/container/ ā€¢ List of all servicesā€Ø https://api.drupal.org/api/drupal/services ā€¢ Services And Dependency Injectionā€Ø https://www.drupal.org/docs/8/api/services-and- dependency-injection
  • 79. NWDUGNORTH WEST DRUPAL USER GROUP 2ND TUESDAY / MONTH STREAM ON YOUTUBE 3RD UNCONFERENCE IN NOVEMBER FOLLOW @NWDUG
  • 81. Phil Norton Technical Lead at Help run NWDUG Blog at #! code (www.hashbangcode.com) @philipnorton42 on Twitter