Upgrade to Pro — share decks privately, control downloads, hide ads and more …

IoC container - Laracon EU 2016

IoC container - Laracon EU 2016

Did you know your IoC container can do a whole lot more than just constructor injection? Besides that it is actually packed with features. Inflectors, resolving callbacks, aliasing, method invocation to name a few. In this talk you will learn how to leverage the power of a great container and service providers to write better, loosely coupled code. Well designed code put together by your IoC container will make your applications SOLID, modular, lean and decoupled from the framework!

Feedback here: https://joind.in/event/laracon-eu-2016/ioc-container-beyond-constructor-injection

39eb3f3d313b13f05534e496285040b8?s=128

Hannes Van De Vreken

August 23, 2016
Tweet

Transcript

  1. IOC container @hannesvdvreken Laracon EU 2016

  2. Hi, my name is Hannes.

  3. !

  4. madewithlove.be !$%&'

  5. IoC Container

  6. I O C

  7. International O C

  8. International Olympic C

  9. International Olympic Committee

  10. International Olympic Committee

  11. WHAT IS IOC? IoC == Inversion of Control

  12. WHAT IS IOC? But first: Design patterns

  13. WHAT IS IOC? class Service { public function __construct(Ftp $ftp)

    { } public function calculate() { // Get stuff from FTP. // Do calculations. // Return result. } }
  14. WHAT IS IOC? class Service { public function __construct(Ftp $ftp)

    { } public function calculate() { // Get stuff from FTP. // Add caching to prevent hitting FTP over and over again // Do calculations. // Return result. } }
  15. WHAT IS IOC? class Service { public function __construct(Ftp $ftp)

    { } public function calculate() { // Get stuff from FTP. // Add caching to prevent hitting FTP over and over again // Log that FTP is accessed. // Do calculations. // Return result. } }
  16. WHAT IS IOC? class Service { public function __construct(Ftp $ftp)

    { } public function calculate() { // Get stuff from FTP. // Add caching to prevent hitting FTP over and over again // Log that FTP is accessed. // Do calculations. // Return result. } }
  17. WHAT IS IOC? \ class FtpStorage { public function __construct(Ftp

    $ftp) { $this->ftp = $ftp; } public function get($key) { // Get it. // Cache it. // Log it. } }
  18. WHAT IS IOC? class Service { public function __construct(FtpStorage $storage)

    {
 $this->storage = $storage; } }
  19. WHAT IS IOC? interface Storage { public function get($key); }

  20. WHAT IS IOC? \ class FtpStorage implements Storage { public

    function __construct(Ftp $ftp) { $this->ftp = $ftp; } public function get($key) { … } }
  21. WHAT IS IOC? class Service { public function __construct(Storage $storage)

    {
 $this->storage = $storage; } }
  22. WHAT IS IOC? new Service(new FtpStorage(new Ftp($settings)));

  23. WHAT IS IOC? Don’t create these instances yourself

  24. WHAT IS IOC? Inversion of Control IoC container to the

    rescue
  25. WHAT IS IOC? \ class CacheDecorator implements Storage { public

    function __construct(Cache $cache, Storage $storage) {…} public function get($key) { return $this->cache->remember($key, 60, function () { return $this->storage->get($key); }); } }
  26. WHAT IS IOC? \ class LogDecorator implements Storage { public

    function __construct(LoggerInterface $log, Storage $storage) {…} public function get($key) { $this->log->info('Retrieved value for '.$key); return $this->storage->get($key); } }
  27. WHAT IS IOC? \ class FtpStorage implements Storage { public

    function __construct(Ftp $ftp) { $this->ftp = $ftp; } public function get($key) { // Get it and return it. } }
  28. WHAT IS IOC? FtpStorage implements <interface> Storage Service depends on

    depends on Ftp
  29. WHAT IS IOC? FtpStorage <interface> Storage CacheDecorator Service LogDecorator S3Storage

    implements depends on
  30. WHAT IS IOC? Dependency inversion: Interface + implementation Dependency injection:

    Injecting `Storage` into `Service` Inversion of Control: The container builds your objects and calls them
  31. WHAT IS IOC? $container->get(Service::class); 1) Make FtpStorage 2) Wrap it

    with LogDecorator 3) Wrap LogDecorator instance with CacheDecorator 4) New up Service with CacheDecorator instance.
  32. WHAT IS IOC? IoC container is here to help you

    with Composition
  33. CONSTRUCTOR INJECTION illuminate/container league/container symfony/dependency-injection aura/di … container-interop/container-interop

  34. Automatic constructor injection

  35. class SendInvoice { public function __construct(Mailer $mailer) { $this->mailer =

    $mailer; } public function listen(InvoiceGeneratedEvent $event) { … } }
  36. CONSTRUCTOR INJECTION Constructor injection

  37. $app->make(SendInvoice::class);

  38. CONSTRUCTOR INJECTION Reflection

  39. (new \ReflectionClass($class)) ->getConstructor() ->getParameters();

  40. CONSTRUCTOR INJECTION Recursively

  41. Helping the container

  42. REGISTRATION Service Providers

  43. class ServiceProvider { public function register() { … } public

    function boot(Dispatcher $events) }
  44. class ServiceProvider { public function register() {…} public function boot(Dispatcher

    $events) { … } }
  45. class ServiceProvider { protected $defer = true; public function register()

    {…} public function provides() { return [ Mailer::class, SwiftMailer::class, ]; } }
  46. REGISTRATION Register bindings in the register method

  47. REGISTRATION - BINDING $this->app->bind( MailerContract::class, SwiftMailer::class );

  48. REGISTRATION - BINDING $this->app->bind(Mailer::class, function () { $mailer = new

    SwiftMailer(); return new LogDecorator($mailer); });
  49. REGISTRATION - BINDING $this->app->singleton(Mailer::class, function () { … });

  50. REGISTRATION - BINDING $this->app->alias( Mailer::class, 'mailer' );

  51. REGISTRATION Bind a concretion for every interface

  52. REGISTRATION Bind everything with its class name

  53. REGISTRATION Unless you bind multiple versions

  54. REGISTRATION $this->app->bind('fs.photos', function () {}); $this->app->bind('fs.files', function () {});

  55. REGISTRATION - CONTEXTUAL BINDING Contextual binding

  56. REGISTRATION - CONTEXTUAL BINDING $this->app ->when(PhotosController::class) ->needs(FilesystemInterface::class) ->give('fs.photos');

  57. REGISTRATION - CONTEXTUAL BINDING No more DB::connection('db1')->table($table)->…;

  58. $this->app->bind('db.1', function () { return $this->app['db'] ->connection('db1'); }); $this->app ->when(EventListener::class)

    ->needs(ConnectionInterface::class) ->give('db.1');
  59. Inflection REGISTRATION - CONTAINER EVENTS

  60. REGISTRATION - CONTAINER EVENTS Inflection (aka “container events”)

  61. REGISTRATION - CONTAINER EVENTS $this->app->resolving( function (Foo $foo) {…} );

  62. use App\Contracts\EnvironmentAware; $this->app->resolving( function (EnvironmentAware $object) { $object->setEnv( $this->app->environment() );

    } );
  63. interface EnvironmentAware { public function setEnv($env); }

  64. trait EnvironmentAware { public function setEnv($env) { $this->env = $env;

    } private function isEnv($envs): bool { return in_array($this->env, (array) $envs); } }
  65. REGISTRATION - CONTAINER EVENTS Method injection

  66. REGISTRATION - CONTAINER EVENTS Also useful for: - Quickly injecting

    stuff
  67. REGISTRATION - CONTAINER EVENTS interface CommandBusAware trait CommandBusAware interface EventDispatcherAware

    trait EventDispatcherAware
  68. REGISTRATION - CONTAINER EVENTS Also useful for: - Quickly injecting

    stuff
  69. REGISTRATION - CONTAINER EVENTS Also useful for: - Quickly injecting

    stuff - Injecting soft dependencies
  70. $this->app->resolving(function (Mailer $mailer) { $mailer->setQueueResolver(function () { return $this->app->make(Queue::class); });

    );
  71. None
  72. REGISTRATION - CONTAINER EVENTS Also useful for: - Quickly injecting

    stuff - Injecting soft dependencies
  73. REGISTRATION - CONTAINER EVENTS Also useful for: - Quickly injecting

    stuff - Injecting soft dependencies - Setting configurable values
  74. $this->app->resolving(function (Validator) { $fileTypes = $app['config']->get(…) $validator->setAllowedFileTypes($fileTypes); );

  75. Using the container

  76. CALLING THE CONTAINER Preferably only for - Factories - Event

    Dispatchers - Router - Command Bus
  77. $this->app->make('fs'); $this->app->make('fs', […]); $this->app->bind( 'fs', function ($app, $args) {…} );

  78. $this->app->call(function (Mailer $mailer) {}); $this->app->call([$listener, 'handle']); $this->app->call('Listener::handle'); $this->app->call('Listener@handle');

  79. $func = $this->app->wrap('Listener@handle'); $func();

  80. public function wrap($callback) { return function () { $this->call($callback); };

    }
  81. Bonus

  82. BONUS - SOFT DEPENDENCIES Solving circular dependencies

  83. BONUS - CIRCULAR DEPENDENCIES Solving circular dependencies

  84. BONUS - CIRCULAR DEPENDENCIES $mailer = new Mailer(QueueInterface $queue); $queue

    = new Queue(MailerInterface $mailer);
  85. BONUS - CIRCULAR DEPENDENCIES $mailer = new Mailer(); $queue =

    new Queue(MailerInterface $mailer); $mailer->setQueueResolver(function () { return $this->app->make('queue'); });
  86. BONUS - NOT SUPPORTED Contextual binding for methods (unsupported)

  87. BONUS - METHOD LEVEL CONTEXTUAL BINDING $this->app—>when('FooController@store') ->needs(QueueContract::class) ->give('queue.redis'); https:/

    /github.com/laravel/framework/pull/12183
  88. recap: Reflection Binding Calling code Bonus

  89. Thank you! https://joind.in/talk/xxxxx @hannesvdvreken Laracon EU 2016

  90. Time for questions. @hannesvdvreken Laracon EU 2016

  91. • http:/ /mwl.be REFERENCES