dependency injection drupal camp wrocław 2014

62
Dependency Injection in Drupal 8 Greg Szczotka Drupal Camp Wrocław 19.10.2014

Upload: greg-szczotka

Post on 25-May-2015

184 views

Category:

Software


1 download

DESCRIPTION

Dependency injection Drupal Camp Wrocław 2014

TRANSCRIPT

Page 1: Dependency injection Drupal Camp Wrocław 2014

Dependency Injection

in Drupal 8 Greg Szczotka

Drupal Camp Wrocław

19.10.2014

Page 2: Dependency injection Drupal Camp Wrocław 2014

About me Greg Szczotka @greg606 ●  Lead Developer @ Creaticon ●  angielski-online.pl (Joomla) ●  PHP trainer @ Quali

Page 3: Dependency injection Drupal Camp Wrocław 2014

Drupal 8, welcome to OO!

Page 4: Dependency injection Drupal Camp Wrocław 2014

Why change is hard? “Object-oriented design is about managing dependencies. It is a set of coding techniques that arrange dependencies such that objects can tolerate change. In the absence of design, unmanaged dependencies wreak havoc because objects know too much about one another. Changing one object forces change upon its collaborators, which in turn, forces change upon its collaborators, ad infinitum. A seemingly insignificant enhancement can cause damage that radiates outward in overlapping concentric circles, ultimately leaving no code untouched. When objects know too much they have many expectations about the world in which they reside. They’re picky, they need things to be “just so.” These expectations constrain them. The objects resist being reused in different contexts; they are painful to test and susceptible to being duplicated.” Sandi Metz. “Practical Object-Oriented Design in Ruby: An Agile Primer (Jason Arnold's Library).”

Page 5: Dependency injection Drupal Camp Wrocław 2014

“Because well designed objects have a single responsibility, their very nature requires that they collaborate to accomplish complex tasks. This collaboration is powerful and perilous. To collaborate, an object must know something know about others. Knowing creates a dependency. If not managed carefully, these dependencies will strangle your application.” Sandi Metz. “Practical Object-Oriented Design in Ruby: An Agile Primer (Jason Arnold's Library).”

Page 6: Dependency injection Drupal Camp Wrocław 2014

"Dependency Injection" is a 25-dollar term for a 5-cent concept.

Dependency Injection

Page 7: Dependency injection Drupal Camp Wrocław 2014

Depenency Injection - Definition « Dependency Injection is where components are given

their dependencies through their constructors, methods, or directly into fields. »

Page 8: Dependency injection Drupal Camp Wrocław 2014

Why DI ? Goal: we want to write code that is...

✔ Clutter-free ✔ Reusable ✔ Testable

Page 9: Dependency injection Drupal Camp Wrocław 2014

An injection is the passing of a dependency (a service) to a dependent object (a client).

The service is made part of the client's state. Passing the

service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the

pattern.

Page 10: Dependency injection Drupal Camp Wrocław 2014

What are dependencies? ●  framework ●  third party libraries ●  database ●  filesystem ●  email ●  web services ●  system resources (Clock) ●  configuration ●  the new keyword ●  static methods ●  random

Page 11: Dependency injection Drupal Camp Wrocław 2014

Before DI class A

{

public function a1() {

$b = new B();

$b->b1();

}

}

Page 12: Dependency injection Drupal Camp Wrocław 2014

class Flashcard { private $db; public function __construct() { $this->db = new new MysqlConnection('flashcards'); } } $flashcard = new Flashcard();

easy to use

hard to customize

Page 13: Dependency injection Drupal Camp Wrocław 2014

Types of DI -  constructor injection -  setter injection -  interface injection -  property injection

Page 14: Dependency injection Drupal Camp Wrocław 2014

DI - Constructor Injection class Flashcard { private $db; public function __construct(MySqlConnection $db) { $this->db = $db; } } $db = new MySQLConnection(); $flashcard = new Flashcard($db);

slightly more difficult to use

Page 15: Dependency injection Drupal Camp Wrocław 2014

DI - Setter Injection class Flashcard { private $db; public setDb(MySqlConnection $db) { $this->db = $db; } }

Page 16: Dependency injection Drupal Camp Wrocław 2014

Dependency injection is a software design pattern that implements inversion of control and

allows a program design to follow the dependency inversion principle.

Page 17: Dependency injection Drupal Camp Wrocław 2014

SOLID ●  Single responsibility, ●  Open-closed, ●  Liskov substitution, ●  Interface segregation, ●  Dependency inversion

Page 18: Dependency injection Drupal Camp Wrocław 2014
Page 19: Dependency injection Drupal Camp Wrocław 2014

Depend on abstractions, not on concretions.

Page 20: Dependency injection Drupal Camp Wrocław 2014

Dependency Inversion Principle

A. High-level modules should not depend on low-level modules.

Both should depend on abstractions.

B. Abstractions should not depend on details.

Details should depend on abstractions.

Page 21: Dependency injection Drupal Camp Wrocław 2014

class Flashcard { private $db; public function __construct(MySqlConnection $db) { $this->db = $db; } }

Page 22: Dependency injection Drupal Camp Wrocław 2014

interface ConnectionInterface { public function connect(); }

class DbConnection implements ConnectionInterface {

public function connect() {

//do something

};

}class Flashcard {

public function __construct(ConnectionInterface $connection){

$this->connection = $connection;

}

} $db = new DbConnection(); $flashcard = new Flashcard($db);

Page 23: Dependency injection Drupal Camp Wrocław 2014

Dependency Injection ==

Inversion of Control

Page 24: Dependency injection Drupal Camp Wrocław 2014

Inversion of Control A design in which custom-written portions of a computer program receive the flow of control from a generic, reusable library. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the reusable code that calls into the custom, or task-specific, code.

Page 25: Dependency injection Drupal Camp Wrocław 2014

IoC = Hollywood Principle "don't call us, we'll call you."

Instead of your program running the system, the system runs your program.

Page 26: Dependency injection Drupal Camp Wrocław 2014

Inversion of Control - Why? ●  To decouple the execution of a task from implementation. ●  To focus a module on the task it is designed for. ●  To free modules from assumptions about how other

systems do what they do and instead rely on contracts. ●  To prevent side effects when replacing a module.

Page 27: Dependency injection Drupal Camp Wrocław 2014

IoC - implementations ●  factory pattern ●  service locator pattern ●  dependency injection ●  contextualized lookup ●  template method pattern ●  strategy pattern

Page 28: Dependency injection Drupal Camp Wrocław 2014

The less your code knows, the more reusable it is.

Page 29: Dependency injection Drupal Camp Wrocław 2014

DI - Problem $transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => 'foo', 'password' => 'bar', 'ssl' => 'ssl', 'port' => 465, )); $mailer = new Zend_Mail(); $mailer->setDefaultTransport($transport);

Page 30: Dependency injection Drupal Camp Wrocław 2014

Dependency Injection Container A Dependency Injection Container is an object that knows how to instantiate and configure objects. And to be able to do its job, it needs to knows about the constructor arguments and the relationships between the objects. These objects are called Services.

Page 31: Dependency injection Drupal Camp Wrocław 2014
Page 32: Dependency injection Drupal Camp Wrocław 2014

How does it work?

➔ Service keys map to service definitions ➔ Definitions specify which class to instantiate and what its dependencies are ➔ Dependencies are specified as references to other services (using service keys) ➔ $container->getService('some_service')

Page 33: Dependency injection Drupal Camp Wrocław 2014

Service Container ➔ Assumes responsibility for constructing object graphs (i.e. instantiating your classes with their dependencies) ➔ Uses configuration data to know how to do this ➔ Allows infrastructure logic to be kept separate from application logic

Page 34: Dependency injection Drupal Camp Wrocław 2014

What is Service? A service is any PHP class that performs an action.

A service is an object that provides some kind of globally useful functionality “A service is an object, registered at the service container under a certain id. ” Matthias Noback. “A Year With Symfony.”

Page 35: Dependency injection Drupal Camp Wrocław 2014

Examples of Services

➔ Cache Backend ➔ Logger ➔ Mailer ➔ URL Generator

Page 36: Dependency injection Drupal Camp Wrocław 2014

Examples of NOT Services

➔ Product ➔ Blog post ➔ Email message

Page 37: Dependency injection Drupal Camp Wrocław 2014

Symfony DI Component The DependencyInjection component allows you to standardize and centralize the way objects are constructed in your application.

Page 38: Dependency injection Drupal Camp Wrocław 2014

Symfony DI Component

●  Default scope: container

●  Can be configured in PHP, XML or YAML

●  Can be “compiled” down to plain PHP

Page 39: Dependency injection Drupal Camp Wrocław 2014

SDIC - Compiling the container

It's too expensive to parse configuration on

every request.

Parse once and put the result into a PHP class

that hardcodes a method for each service.

Page 40: Dependency injection Drupal Camp Wrocław 2014

SDIC - Compiling the container serialization.json:

class: Drupal\Component\Serialization\Json

class service_container extends Container {

public function getSerialization_JsonService()

{ return $this->services['serialization.json'] =

new \Drupal\Component\Serialization\Json();

}

}

Page 41: Dependency injection Drupal Camp Wrocław 2014

SDIC - Compiling the container public function getStateSevice()

{ return $this->services['state'] =

new \Drupal\Core\State\State(

$this->get('keyvalue')

);

}

Page 42: Dependency injection Drupal Camp Wrocław 2014

Some D8 Services ➔ The default DB connection ('database') ➔ The module handler ('module_handler') ➔ The HTTP request object ('request')

Page 43: Dependency injection Drupal Camp Wrocław 2014

D8 Core Services CoreServiceProvider.php and core.services.yml language_manager:

class: Drupal\Core\Language\LanguageManager

arguments: ['@language.default']

path.alias_manager:

class: Drupal\Core\Path\AliasManager

arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager']

string_translation:

class: Drupal\Core\StringTranslation\TranslationManager

breadcrumb:

class: Drupal\Core\Breadcrumb\BreadcrumbManager

arguments: ['@module_handler']

Page 44: Dependency injection Drupal Camp Wrocław 2014

Don't inject the container! Ever.

(Unless you absolutely must)

Page 45: Dependency injection Drupal Camp Wrocław 2014

Service Definition module_name.services.yml

services:

demo.demo_service:

class: Drupal\demo\DemoService

Page 46: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Arguments module_handler:

class: Drupal\Core\Extension\ModuleHandler

arguments: ['%container.modules%', '@cache.bootstrap']

Page 47: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Calls class_resolver:

class: Drupal\Core\DependencyInjection\ClassResolver

calls:

- [setContainer, ['@service_container']]

Page 48: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Factory database:

class: Drupal\Core\Database\Connection

factory_class: Drupal\Core\Database\Database

factory_method: getConnection

arguments: [default]

Page 49: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Factory Service cache.default:

class: Drupal\Core\Cache\CacheBackendInterface

tags:

- { name: cache.bin }

factory_method: get

factory_service: cache_factory

arguments: [default]

Page 50: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Alias

twig.loader:

alias: twig.loader.filesystem

Page 51: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Abstract/Parent default_plugin_manager:

abstract: true

arguments: ['@container.namespaces', '@cache.discovery',

'@module_handler']

plugin.manager.archiver:

class: Drupal\Core\Archiver\ArchiverManager

parent: default_plugin_manager

Page 52: Dependency injection Drupal Camp Wrocław 2014

Service Definition - Public config.storage.active:

class: Drupal\Core\Config\DatabaseStorage

arguments: ['@database', 'config']

public: false

tags:

- { name: backend_overridable }config.storage.file:

class: Drupal\Core\Config\FileStorage

factory_class: Drupal\Core\Config\FileStorageFactory

factory_method: getActive

public: false

Page 53: Dependency injection Drupal Camp Wrocław 2014

D8 - how to get service? ●  Drupal::getContainer()->get('url_generator') ●  Drupal::service('url_generator') ●  Drupal::urlGenerator()

please don’t!

INJECT LIKE HELL

Page 54: Dependency injection Drupal Camp Wrocław 2014

D8 - Service Implementation namespace Drupal\demo;

class DemoService {

protected $demo_value;

public function __construct() {

$this->demo_value = 'Upchuk';

}

public function getDemoValue() { return $this-

>demo_value;

}

}

Page 55: Dependency injection Drupal Camp Wrocław 2014

D8 - Controller class DemoController extends ControllerBase {

public function demo() { return array(

'#markup' => t('Hello @value!', array('@value' => $this-

>demoService->getDemoValue())),

);

}

}

Page 56: Dependency injection Drupal Camp Wrocław 2014

D8 - Controller class DemoController extends ControllerBase { protected $demoService; public function __construct($demoService) { $this->demoService = $demoService; } public static function create(ContainerInterface $container) { return new static( $container->get('demo.demo_service') ); } }

Page 57: Dependency injection Drupal Camp Wrocław 2014

D8 - DI for a form class ExampleForm extends FormBase { public function __construct(AccountInterface $account) { $this->account = $account; } public static function create(ContainerInterface $container) { // Instantiates this form class. return new static( // Load the service required to construct this class. $container->get('current_user') ); } }

Page 58: Dependency injection Drupal Camp Wrocław 2014

D8 - Altering existing services, providing dynamic services namespace Drupal\my_module;use Drupal\Core\DependencyInjection

\ContainerBuilder;use Drupal\Core\DependencyInjection

\ServiceProviderBase;class MyModuleServiceProvider extends

ServiceProviderBase {

public function alter(ContainerBuilder $container) {

// Overrides language_manager class to test domain language

negotiation.

$definition = $container->getDefinition('language_manager');

$definition->setClass('Drupal\language_test\LanguageTestManager');

}

}

Page 59: Dependency injection Drupal Camp Wrocław 2014

Service tags services:

foo.twig.extension:

class: Dcwroc\HelloBundle\Extension\FooExtension

tags:

- { name: twig.extension }

Page 60: Dependency injection Drupal Camp Wrocław 2014

D8 Service Tags access_check authentication_provider

breadcrumb_builder

cache.bin cache.context config.factory.override

encoder entity_resolver event_subscriber needs_destruction normalizer

paramconverter path_processor_inbound persist plugin_manager_cache_clear route_enhancer route_filter route_processor_outbound string_translator theme_negotiator twig.extension

Page 61: Dependency injection Drupal Camp Wrocław 2014

Resources ●  Inversion of Control Containers and the Dependency

Injection pattern

●  Symfony Service Container

●  The DependencyInjection Component

●  Services and dependency injection in Drupal 8

Page 62: Dependency injection Drupal Camp Wrocław 2014