dependency injection in drupal 8
DESCRIPTION
TRANSCRIPT
![Page 1: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/1.jpg)
Dependency Injection in Drupal 8
By Alexei Gorobetsasgorobets
![Page 2: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/2.jpg)
Why talk about DI?
* Drupal 8 is coming…* DI is a commonly used pattern in software design
![Page 3: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/3.jpg)
The problems in D7
1. Strong dependencies* Globals: - users
- database- language- paths
* EntityFieldQuery still assumes SQL in property queries* Views assumes SQL database.
![Page 4: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/4.jpg)
The problems in D7
2. No way to reuse.
* Want to change a small part of contrib code? Copy the callback entirely and replace it with your code.
![Page 5: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/5.jpg)
The problems in D7
3. A lot of clutter happening.
* Use of preprocess to do calculations.* Excessive use of alter hooks for some major replacements.* PHP in template files? Huh =)
![Page 6: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/6.jpg)
The problems in D7
4. How can we test something?
* Pass dummy DB connection? NO.* Create mock objects? NO.* Want a simple test class?Bootstrap Drupal first.
![Page 7: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/7.jpg)
How we want our code to look like?
![Page 8: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/8.jpg)
![Page 9: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/9.jpg)
How it actually looks?
![Page 10: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/10.jpg)
![Page 11: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/11.jpg)
This talk is about
* DI Design Pattern* DI in Symfony* DI in Drupal 8
![Page 12: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/12.jpg)
Our goal:
![Page 13: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/13.jpg)
Let’s take a wrong approach
![Page 14: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/14.jpg)
Some procedural code
function foo_bar($foo) { global $user; if ($user->uid != 0) { $nodes = db_query("SELECT * FROM {node}")->fetchAll(); } // Load module file that has the function. module_load_include('inc', cool_module, 'cool.admin'); node_make_me_look_cool(NOW());}
![Page 15: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/15.jpg)
Some procedural code
function foo_bar($foo) { global $user; if ($user->uid != 0) { $nodes = db_query("SELECT * FROM {node}")->fetchAll(); } // Load module file that has the function. module_load_include('inc', cool_module, 'cool.admin'); node_make_me_look_cool(NOW());}
* foo_bar knows about the user object.
* node_make_me_look_cool needs a function from another module.
* You need bootstrapped Drupal to use module_load_include, or at least include the file that declares it.
* foo_bar assumes some default database connection.
![Page 16: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/16.jpg)
Let’s try an OO approach
![Page 17: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/17.jpg)
User class uses sessions to store language
class SessionStorage{ function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); } function set($key, $value) { $_SESSION[$key] = $value; } function get($key) { return $_SESSION[$key]; } // ...}
class User{ protected $storage; function __construct() { $this->storage = new SessionStorage(); } function setLanguage($language) { $this->storage->set('language', $language); } function getLanguage() { return $this->storage->get('language'); } // ...}
![Page 18: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/18.jpg)
Working with User$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();
Working with such code is damn easy.
What could possibly go wrong here?
![Page 19: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/19.jpg)
What if?What if we want to change a session cookie name?
![Page 20: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/20.jpg)
What if?What if we want to change a session cookie name?
class User{ function __construct() { $this->storage = new SessionStorage('SESSION_ID'); } // ...}
Hardcode the name in User’s constructor
![Page 21: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/21.jpg)
What if?
class User{ function __construct() { $this->storage = new SessionStorage(STORAGE_SESSION_NAME); } // ...} define('STORAGE_SESSION_NAME', 'SESSION_ID');
Define a constant
What if we want to change a session cookie name?
![Page 22: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/22.jpg)
What if?
class User{ function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } // ...} $user = new User('SESSION_ID');
Send cookie name as User argument
What if we want to change a session cookie name?
![Page 23: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/23.jpg)
What if?
class User{ function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions['session_name']); } // ...} $user = new User(array('session_name' => 'SESSION_ID'));
Send an array of options as User argument
What if we want to change a session cookie name?
![Page 24: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/24.jpg)
What if?What if we want to change a session storage?
For instance to store sessions in DB or files
![Page 25: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/25.jpg)
What if?What if we want to change a session storage?
For instance to store sessions in DB or files
You need to change User class
![Page 26: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/26.jpg)
Introducing Dependency Injection
![Page 27: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/27.jpg)
![Page 28: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/28.jpg)
Here it is:
class User{ function __construct($storage) { $this->storage = $storage; } // ...}
Instead of instantiating the storage in User, let’s just pass it from outside.
![Page 29: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/29.jpg)
Here it is:
class User{ function __construct($storage) { $this->storage = $storage; } // ...}
Instead of instantiating the storage in User, let’s just pass it from outside.
$storage = new SessionStorage('SESSION_ID');$user = new User($storage);
![Page 30: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/30.jpg)
Types of injections:class User{ function __construct($storage) { $this->storage = $storage; }}
class User{ function setSessionStorage($storage) { $this->storage = $storage; }}
class User{ public $sessionStorage;} $user->sessionStorage = $storage;
Constructor injection
Setter injection
Property injection
![Page 31: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/31.jpg)
Ok, where does injection happen?
$storage = new SessionStorage('SESSION_ID');$user = new User($storage);
Where should this code be?
![Page 32: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/32.jpg)
Ok, where does injection happen?
$storage = new SessionStorage('SESSION_ID');$user = new User($storage);
Where should this code be?
* Manual injection* Injection in a factory class* Using a container/injector
![Page 33: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/33.jpg)
How frameworks do that?
Dependency Injection Container (DIC)or
Service container
![Page 34: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/34.jpg)
What is a Service?
![Page 35: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/35.jpg)
Service is any PHP object that performs some sort of "global"
task
![Page 36: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/36.jpg)
Let’s say we have a class
class Mailer{ private $transport;
public function __construct($transport) { $this->transport = $transport; }
// ...}
![Page 37: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/37.jpg)
How to make it a service?
![Page 38: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/38.jpg)
Using YAMLparameters: mailer.class: Mailer mailer.transport: sendmail
services: mailer: class: "%mailer.class%" arguments: ["%my_mailer.transport%"]
![Page 39: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/39.jpg)
Using YAMLparameters: mailer.class: Mailer mailer.transport: sendmail
services: mailer: class: "%mailer.class%" arguments: ["%my_mailer.transport%"]
Loading a service from yml file.
require_once '/PATH/TO/sfServiceContainerAutoloader.php';sfServiceContainerAutoloader::register(); $sc = new sfServiceContainerBuilder(); $loader = new sfServiceContainerLoaderFileYaml($sc);$loader->load('/somewhere/services.yml');
![Page 40: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/40.jpg)
Use PHP Dumper to create PHP file once$name = 'Project'.md5($appDir.$isDebug.$environment).'ServiceContainer';$file = sys_get_temp_dir().'/'.$name.'.php'; if (!$isDebug && file_exists($file)){ require_once $file; $sc = new $name();}else{ // build the service container dynamically $sc = new sfServiceContainerBuilder(); $loader = new sfServiceContainerLoaderFileXml($sc); $loader->load('/somewhere/container.xml'); if (!$isDebug) { $dumper = new sfServiceContainerDumperPhp($sc); file_put_contents($file, $dumper->dump(array('class' => $name)); }}
![Page 41: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/41.jpg)
Use Graphviz dumper for this beauty
![Page 42: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/42.jpg)
Symfony terminology
![Page 43: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/43.jpg)
Symfony terminology
![Page 44: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/44.jpg)
Compile the containerThere is no reason to pull configurations on every
request. We just compile the container in a PHP class with hardcoded methods for each service.
container->compile();
![Page 45: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/45.jpg)
Compiler passes
Compiler passes are classes that process the container, giving you an opportunity to manipulate existing service definitions.
Use them to:
● Specify a different class for a given serviceid ● Process“tagged”services
![Page 46: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/46.jpg)
Compiler passes
![Page 47: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/47.jpg)
Tagged services
There is a way to tag services for further processing in compiler passes.
This technique is used to register event subscribers to Symfony’s event dispatcher.
![Page 48: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/48.jpg)
Tagged services
![Page 49: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/49.jpg)
Event subscribers
![Page 50: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/50.jpg)
Event subscribers
![Page 51: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/51.jpg)
Changing registered service
![Page 52: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/52.jpg)
1. Write OO code and get wired into the container.
2. In case of legacy procedural code you can use:
Drupal::service(‘some_service’);
Example:
Drupal 7:
$path = $_GET['q']
Drupal 8:
$request = Drupal::service('request');
$path = $request->attributes->get('_system_path');
Ways to use core’s services
![Page 53: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/53.jpg)
● The Drupal Kernel : core/lib/Drupal/Core/DrupalKernel.php
● Services are defined in: core/core.services.yml
● Compiler passes get added in: core/lib/Drupal/Core/CoreServiceProvider.php
● Compiler pass classes are in: core/lib/Drupal/Core/DependencyInjection/Compiler/
Where the magic happens?
![Page 54: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/54.jpg)
● Define module services in : mymodule/mymodule.services.yml
● Compiler passes get added in: mymodule/lib/Drupal/mymodule/MymoduleServiceProvider.php
● All classes including Compiler class classes live in: mymodule/lib/Drupal/mymodule
What about modules?
![Page 55: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/55.jpg)
Resources:
● Fabien Potencier’s “What is Dependency Injection” series
● Symfony Service Container● Symfony Dependency Injection Component● WSCCI Conversion Guide● Change notice: Use Dependency Injection to
handle global PHP objects
![Page 56: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/56.jpg)
$slide = new Questions();
$current_slide = new Slide($slide);
![Page 57: Dependency injection in Drupal 8](https://reader030.vdocuments.net/reader030/viewer/2022020306/54b7a49c4a79591d4a8b4a20/html5/thumbnails/57.jpg)