dependency injection in zf2 - akrabat.com · dependency injection enables loose coupling and loose...

54
Dependency Injection in ZF2 Rob Allen ~ October 2014

Upload: vuongduong

Post on 04-Jun-2018

239 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Dependency Injectionin ZF2

Rob Allen ~ October 2014

Page 2: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Dependency Injection enables loose coupling andloose coupling makes code more maintainable

Mark Seemann

Page 3: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Coupling

Page 4: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Benefits of loose coupling• Maintainability - Classes are more clearly defined• Extensibility - easy to recompose application• Testability - isolate what you’re testing

Page 5: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

A worked exampleClass A needs class B in order to work.

class Letter

{

protected $paper;

public function __construct()

{

$this->paper = new WritingPaper();

}

}

// usage:

$letter = new Letter();

$letter->write("Dear John, ...");

Page 6: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Pros and cons:Pros:

• Very simple to use

Cons:

• Cannot test Letter in isolation• Cannot change $paper

This is tight coupling

Page 7: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

The problem with coupling• How do we change the paper size?• How do we change the type of paper?

Page 8: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Method parameters?class Letter

{

protected $paper;

public function __construct($size)

{

$this->paper = new WritingPaper($size);

}

}

// usage:

$letter = new Letter('A4');

$letter->write("Dear John, ...");

Page 9: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Use a Registry?class Letter

{

protected $paper;

public function __construct()

{

$this->paper = Zend_Registry::get('paper');

}

}

// usage:

Zend_Registry::set('paper', new AirmailPaper('A4'));

$letter = new Letter();

$letter->write("Dear John, ...");

Page 10: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Inject the dependency!

Page 11: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Injectionclass Letter

{

protected $paper;

public function __construct($paper)

{

$this->paper = $paper;

}

}

// usage:

$letter = new Letter(new WritingPaper('A4'));

$letter->write("Dear John, ...");

Page 12: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

This is also known as

Inversion of Control

Page 13: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Pros and cons:Pros:

• Decoupled $paper from Letter:

• Can change the type of paper• Natural configuration of the Paper object

• Can test Letter independently

Cons:

• Burden of construction of $paper is on the user

Page 14: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Types of injectionConstructor injection:

$letter = new Letter($paper);

Setter injection:

$letter = new Letter();

$letter->setPaper($paper);

Interface injection:

$letter = new Letter();

if (!$letter instanceof PaperInterface) {

$letter->setPaper(new WritingPaper())

}

Page 15: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

NoteToo many constructor parameters is a code smell

Two-phase construction is Bad(TM)

Page 16: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Rule of thumb• Constructor injection for required dependencies• Setter injection for optional dependencies

Page 17: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

How about usage?$paper = new AirmailPaper('A4');

$envelope = new Envelope('DL');

$letter = new Letter($paper, $envelope);

$letter->write("Dear John, ...");

Setup of dependencies gets tedious quickly

Page 18: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Dependency Injection Container

A DIC is an object that handles the creation of objects andtheir dependencies for you

Dependency resolution can be automatic or configured

DICs are optional

Page 19: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Dependency Injection Container

• Creates objects on demand• Manages construction of an object’s dependencies• Separates of configuration from construction• Can allow for shared objects

Page 20: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

That’s all there is to DI

Page 21: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Remember that I said thatDICs are optional?

Page 22: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Not in ZF2, they’re not!

Page 23: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Zend\ServiceManager• ZF2’s Dependency Injection Container• Used extensively within ZF2• Explicit & easy to understand (no magic!)• Promotes low-coupling & re-usability• Easy to swap out ZF2 classes with your own

Page 24: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

The process

1. Register your services2. The Zend\Mvc operation results in your services being

instantiated as required3. Your app runs and does it’s stuff!

Page 25: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Registering servicesConfigure your services:

1. in an array in a config file2. in a method within a Module class3. direct method call

Page 26: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

in config// Application/config/module.config.php:

return [

'service_manager' => [

'invokables' => [

'session' => 'Zend\Session\Storage',

],

'factories' => [

'db' => 'My\DBAL\DriverManagerFactory',

],

]

];

Page 27: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

in a Module class// Application::Module

public function getServiceConfig()

{

return [

'factories' => [

'UserMapper' => function ($sm) {

$db = $sm->get('db');

return new UserMapper($db);

},

],

];

}

Page 28: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Types of services

Instances services

Constructor-less classes invokables

Objects with dependencies factories

Aliased services aliases

Automated initialization initializers

Multiple related objects abstract_factories

Page 29: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Instances// programmatically

$sm->setService('foo', $fooInstance);

// configuration

'services' => [

'foo' => new Foo(),

]

Page 30: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Invokables// programmatically

$sm->setInvokableClass('foo', 'Bar\Foo');

// configuration

'invokables' => [

'foo' => 'Bar\Foo',

]

Page 31: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Factories// programmatically

$sm->setFactory('foo', function($sm) {

$dependency = $sm->get('Dependency')

return new Foo($dependency);

});

// configuration

'factories' => [

'foo' => function($sm) { //.. },

'bar' => 'Some\Static::method',

'baz' => 'Class\Implementing\FactoryInterface',

'bat' => 'Class\Implementing\Invoke',

]

Page 32: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Aliases// programmatically

$sm->setAlias('foo_db', 'db_adapter');

// configuration

'aliases' => [

'foo_db', 'db_adapter', // alias of a service

'bar_db', 'foo_db', // alias of an alias

]

// All the same instance

$db = $sm->get('db_adapter');

$db = $sm->get('foo_db');

$db = $sm->get('bar_db');

Page 33: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Initializers// programmatically

$sm->addInitializer($callback);

// configuration

'initializers' => [

$instance,

$callback,

'Class\Implementing\InitializerInterface',

'Class\That\Implements\__invoke',

]

Page 34: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

An initializerfunction($instance, $sm) {

if ($instance instanceof FooAwareInterface) {

return;

}

$instance->setFoo($sm->get('foo'));

},

Page 35: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Abstract factoriesarray(

'abstract_factories' => [

'Class\Implementing\AbstractFactoryInterface'

$someAbstractFactoryInstance,

]

);

Page 36: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

An abstract factoryclass MyClassLoader implements AbstractFactoryInterface

{

public function canCreateServiceWithName(

ServiceLocatorInterface $services, $name,

$requestedName

) {

// return true or false

}

public function createServiceWithName(/* same sig */)

{

// return instance required

}

}

Page 37: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Real-world configuration'service_manager' => [

'invokables' => [

'Comment\CommentMapper' => 'Comment\CommentMapper',

],

'factories' => [

'Zend\Db\Adapter\Adapter' =>

'Zend\Db\Adapter\AdapterServiceFactory',

'site_navigation' =>

'Application\NavigationSiteNavigationFactory',

],

],

Page 38: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Application\Modulepublic function getServiceConfig()

{

return [

'factories' => [

'LogWriter' => function ($sm) {

$file = 'log_' . date('F') . '.txt';

return new LogWriterStream("var/log/$file");

},

'Zend\Log' => function ($sm) {

$log = new Logger();

$log->addWriter($sm->get('LogWriter');

return $log;

},

],

];

}

Page 39: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

User\Modulepublic function getServiceConfig()

{

return [

'initializers' => [

function ($instance, $sm) {

if ($instance instanceof UserAwareInterface) {

$authService = $sm->get('zfcuser_auth_service');

$user = $authService->getIdentity();

$instance->setUser($user);

}

}

],

];

}

Page 40: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Using our services in acontroller

Page 41: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Controller configuration// module.config.php

'controllers' => [

'invokables' => [

'Application\Controller\Index' =>

'Application\Controller\IndexController',

'Application\Controller\Blog' =>

'Application\Controller\BlogController',

],

],

Controller set-up is simply another service manager!

Page 42: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Inject into your controllerpublic function getControllerConfig()

{

return [

'factories' => [

'Application\Controller\Blog' => function ($csm) {

$sm = $csm->getServiceLocator();

$blogs = $sm->get('Application\BlogMapper');

$comments = $sm->get('Comment\Mapper');

return new BlogController($blogs, $comments);

},

],

];

}

Page 43: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Everything is aServiceManager!

Page 44: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Top 10

Name Config keyServiceManager service_manager

ControllerManager controllers

ControllerPluginManager controller_plugins

ViewHelperManager view_helpers

FormElementManager form_elements

InputFilterManager input_filters

FilterManager filters

ValidatorManager validators

RoutePluginManager route_manager

HydratorPluginManager hydrators

Page 45: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

There are 42 pluginmanagers in ZF2

Page 46: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

View helpers'view_helpers' => array(

'invokables' => array(

'formRow' => 'Application\View\Helper\FormRow',

),

'factories' => array(

'lastComment' => 'Comment\View\Helper\LastComment',

),

),

Page 47: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

View helpersclass LastComment implements FactoryInterface

{

public function createService(

ServiceLocatorInterface $serviceLocator)

{

$locator = $sm->getServiceLocator();

$mapper = $locator->get('Comment\CommentMapper');

return new LastComment($mapper);

}

}

Page 48: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Learn once,reuse everywhere!

Page 49: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

A note on Service Locationclass BlogController extends AbstractActionController

{

protected $mapper;

public function __construct()

{

$sm = $this->getServiceLocator();

$this->mapper = $sm->get('Blog\BlogMapper');

}

}

Page 50: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Service Location• Application pulls its dependencies in when it needs

them• Still decouples concrete implementations

Page 51: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Don’t do this!

Page 52: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

RecapDependency injection promotes:

• loose coupling• easier testing• separation of configuration from usage

Page 53: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Recap

Zend\ServiceManager is used everywhereSix ways to configure:

• invokables• factories• aliases• initializers• services• abstract_factories

Page 54: Dependency Injection in ZF2 - akrabat.com · Dependency Injection enables loose coupling and loose coupling makes code more maintainable Mark Seemann

Thank you!https://m.joind.in/talk/66651

Rob Allen - http://akrabat.com - @akrabat