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 define a common interface.
• Automatic Dependency Injection.
• Powerful
• Certified awesome
config
cron
renderer
path
route
file_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 defined in yml files.
<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.
'@config.factory' == Another service.
'%container.modules%' == A configuration item.
'config' 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 defines 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 defined in
*.services.yml files.
• 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
definition file.
<module>.services.yml
services:
<service name>:
class: Drupal<full namespace of class>
arguments: ['@config.factory', ...]
Service Argument
Types.
'@config.factory' == Another service.
'%container.modules%' == A configuration item.
'config' 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. Suffixed
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.
• Suffixed 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.
• Suffixed 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
find() /
retrieve()API
Address
finder
find() /
retrieve()
Address
finder
Stub Modules.
• Stub modules are useful for:
• Prevent analytics being sent.
• Bypass complex setups / firewalls.
• 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 filters to allow the user list to be
filtered.
• Group context was loaded into the View using a
contextual filter 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

What's hot

Grails Simple Login
Grails Simple LoginGrails Simple Login
Grails Simple Login
moniguna
 

What's hot (20)

Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
Promises, promises, and then observables
Promises, promises, and then observablesPromises, promises, and then observables
Promises, promises, and then observables
 
Introduction à React
Introduction à ReactIntroduction à React
Introduction à React
 
Java persistence api 2.1
Java persistence api 2.1Java persistence api 2.1
Java persistence api 2.1
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
AEM target Integration
AEM target IntegrationAEM target Integration
AEM target Integration
 
RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019RxJS & Angular Reactive Forms @ Codemotion 2019
RxJS & Angular Reactive Forms @ Codemotion 2019
 
Introduction for Master Class "Amazing Reactive Forms"
Introduction for Master Class "Amazing Reactive Forms"Introduction for Master Class "Amazing Reactive Forms"
Introduction for Master Class "Amazing Reactive Forms"
 
Angular
AngularAngular
Angular
 
AEM (CQ) Dispatcher Security and CDN+Browser Caching
AEM (CQ) Dispatcher Security and CDN+Browser CachingAEM (CQ) Dispatcher Security and CDN+Browser Caching
AEM (CQ) Dispatcher Security and CDN+Browser Caching
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome Town
 
Grails Simple Login
Grails Simple LoginGrails Simple Login
Grails Simple Login
 
P3 listes et elements graphiques avancés
P3 listes et elements graphiques avancésP3 listes et elements graphiques avancés
P3 listes et elements graphiques avancés
 
Angular and The Case for RxJS
Angular and The Case for RxJSAngular and The Case for RxJS
Angular and The Case for RxJS
 
Osgi
OsgiOsgi
Osgi
 
The Django Web Application Framework 2
The Django Web Application Framework 2The Django Web Application Framework 2
The Django Web Application Framework 2
 
Belajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data MahasiswaBelajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data Mahasiswa
 
Angular Directives
Angular DirectivesAngular Directives
Angular Directives
 
Applications Android - cours 10 : Vues à adaptateur
Applications Android - cours 10 : Vues à adaptateurApplications Android - cours 10 : Vues à adaptateur
Applications Android - cours 10 : Vues à adaptateur
 
MVC - Introduction
MVC - IntroductionMVC - Introduction
MVC - Introduction
 

Similar to Drupal 8 Services And Dependency Injection

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-2010
Fabien Potencier
 
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
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
jsmith92
 

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

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

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Recently uploaded (20)

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
 
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
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
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
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
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...
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
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...
 
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
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
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
 
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
 

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 define a common interface. • Automatic Dependency Injection. • Powerful • Certified 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 defined in yml files. <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. '@config.factory' == Another service. '%container.modules%' == A configuration item. 'config' 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 defines 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 defined in *.services.yml files. • 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. '@config.factory' == Another service. '%container.modules%' == A configuration item. 'config' 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. Suffixed 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. • Suffixed 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. • Suffixed 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 / firewalls. • 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 filters to allow the user list to be filtered. • Group context was loaded into the View using a contextual filter 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