dependency injection drupal camp wrocław 2014

Post on 25-May-2015

184 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Dependency injection Drupal Camp Wrocław 2014

TRANSCRIPT

Dependency Injection

in Drupal 8 Greg Szczotka

Drupal Camp Wrocław

19.10.2014

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

Drupal 8, welcome to OO!

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).”

“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).”

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

Dependency Injection

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

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

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

✔ Clutter-free ✔ Reusable ✔ Testable

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.

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

Before DI class A

{

public function a1() {

$b = new B();

$b->b1();

}

}

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

easy to use

hard to customize

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

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

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

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

allows a program design to follow the dependency inversion principle.

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

Depend on abstractions, not on concretions.

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.

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

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);

Dependency Injection ==

Inversion of Control

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.

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

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

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.

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

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

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);

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.

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')

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

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.”

Examples of Services

➔ Cache Backend ➔ Logger ➔ Mailer ➔ URL Generator

Examples of NOT Services

➔ Product ➔ Blog post ➔ Email message

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

Symfony DI Component

●  Default scope: container

●  Can be configured in PHP, XML or YAML

●  Can be “compiled” down to plain PHP

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.

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();

}

}

SDIC - Compiling the container public function getStateSevice()

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

new \Drupal\Core\State\State(

$this->get('keyvalue')

);

}

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

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']

Don't inject the container! Ever.

(Unless you absolutely must)

Service Definition module_name.services.yml

services:

demo.demo_service:

class: Drupal\demo\DemoService

Service Definition - Arguments module_handler:

class: Drupal\Core\Extension\ModuleHandler

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

Service Definition - Calls class_resolver:

class: Drupal\Core\DependencyInjection\ClassResolver

calls:

- [setContainer, ['@service_container']]

Service Definition - Factory database:

class: Drupal\Core\Database\Connection

factory_class: Drupal\Core\Database\Database

factory_method: getConnection

arguments: [default]

Service Definition - Factory Service cache.default:

class: Drupal\Core\Cache\CacheBackendInterface

tags:

- { name: cache.bin }

factory_method: get

factory_service: cache_factory

arguments: [default]

Service Definition - Alias

twig.loader:

alias: twig.loader.filesystem

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

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

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

please don’t!

INJECT LIKE HELL

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;

}

}

D8 - Controller class DemoController extends ControllerBase {

public function demo() { return array(

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

>demoService->getDemoValue())),

);

}

}

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') ); } }

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') ); } }

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');

}

}

Service tags services:

foo.twig.extension:

class: Dcwroc\HelloBundle\Extension\FooExtension

tags:

- { name: twig.extension }

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

Resources ●  Inversion of Control Containers and the Dependency

Injection pattern

●  Symfony Service Container

●  The DependencyInjection Component

●  Services and dependency injection in Drupal 8

top related