Save 37% off PRO during our Black Friday Sale! »

IoC container - Laravel Live India

IoC container - Laravel Live India

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!

39eb3f3d313b13f05534e496285040b8?s=128

Hannes Van De Vreken

March 19, 2017
Tweet

Transcript

  1. IOC container @hannesvdvreken LaravelLive India 2017

  2. Hi, my name is Hannes.

  3. !

  4. None
  5. madewithlove.be !%&'(

  6. IoC Container

  7. I O C

  8. International O C

  9. International Olympic C

  10. International Olympic Committee

  11. International Olympic Committee

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

  13. But first: Design patterns WHAT IS IOC?

  14. class Service { public function __construct(Ftp $ftp) { } public

    function calculate() { // Get stuff from FTP. // Do calculations. // Return result. } } WHAT IS IOC?
  15. 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. } } WHAT IS IOC?
  16. 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. } } WHAT IS IOC?
  17. 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. } } WHAT IS IOC?
  18. class FtpStorage { public function __construct(Ftp $ftp) { $this->ftp =

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

    $storage; } } WHAT IS IOC?
  20. interface Storage { public function get($key); } WHAT IS IOC?

  21. class FtpStorage implements Storage { public function __construct(Ftp $ftp) {

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

    $storage; } } WHAT IS IOC?
  23. new Service(new FtpStorage(new Ftp($settings))); WHAT IS IOC?

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

  25. Inversion of Control IoC container to the rescue WHAT IS

    IOC?
  26. 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); }); } } WHAT IS IOC?
  27. 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); } } WHAT IS IOC?
  28. class FtpStorage implements Storage { public function __construct(Ftp $ftp) {

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

    WHAT IS IOC?
  30. FtpStora <interface> Storage CacheDecor Service LogDecora S3Storage implements depends on

    WHAT IS IOC?
  31. Dependency inversion: Interface + implementation Dependency injection: Injecting `Storage` into

    `Service` Inversion of Control: The container builds your objects and calls them WHAT IS IOC?
  32. $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. WHAT IS IOC?
  33. IoC container is here to help you with Composition WHAT

    IS IOC?
  34. illuminate/container league/container symfony/dependency-injection aura/di zendframework/zend-di … container-interop/container-interop psr/container (PSR-11) CONSTRUCTOR

    INJECTION
  35. Automatic constructor injection

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

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

  38. $app->make(SendInvoice::class); CONSTRUCTOR INJECTION

  39. Reflection CONSTRUCTOR INJECTION

  40. (new \ReflectionClass($class)) ->getConstructor() ->getParameters(); CONSTRUCTOR INJECTION

  41. Recursively CONSTRUCTOR INJECTION

  42. Helping the container

  43. Service Providers REGISTRATION

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

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

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

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

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

  49. $this->app->bind(Mailer::class, function () { $mailer = new SwiftMailer(); return new

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

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

  52. Bind a concretion for every interface REGISTRATION - BINDING

  53. Bind everything with its class name REGISTRATION - BINDING

  54. Unless you bind multiple versions REGISTRATION - BINDING

  55. $this->app->bind('fs.photos', function () $this->app->bind('fs.files', function () { REGISTRATION - BINDING

  56. Contextual binding REGISTRATION - CONTEXTUAL BINDING

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

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

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

    ->needs(ConnectionInterface::class) ->give('db.1'); REGISTRATION - CONTEXTUAL BINDING
  60. Inflection REGISTRATION

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

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

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

    } ); REGISTRATION - CONTAINER EVENTS
  64. interface EnvironmentAware { public function setEnv($env); } REGISTRATION - CONTAINER

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

    } private function isEnv($envs): bool { return in_array($this->env, (array) $envs); } } REGISTRATION - CONTAINER EVENTS
  66. public function boot() { $this->app->resolving(function (LoggerAwareInterface $loggingObject) { $logger =

    $this->app->make(PsrLoggerInterface::class); $loggingObject->setLogger($logger); }); } REGISTRATION - CONTAINER EVENTS
  67. Method injection REGISTRATION - CONTAINER EVENTS

  68. Also useful for: - Quickly injecting stuff REGISTRATION - CONTAINER

    EVENTS
  69. interface CommandBusAware trait CommandBusAware interface EventDispatcherAware trait EventDispatcherAware REGISTRATION -

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

    EVENTS
  71. Also useful for: - Quickly injecting stuff - Injecting soft

    dependencies REGISTRATION - CONTAINER EVENTS
  72. $this->app->resolving(function (Mailer $mailer) $mailer->setQueueResolver(function () { return $this->app->make(Queue::class); }); );

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

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

    dependencies - Setting configurable values REGISTRATION - CONTAINER EVENTS
  76. $this->app->resolving(function (Validator) { $fileTypes = $app['config']->get(…) $validator->setAllowedFileTypes($fileTypes) ); REGISTRATION -

    CONTAINER EVENTS
  77. Using the container

  78. Preferably only for - Factories - Event Dispatchers - Router

    - Command Bus CALLING THE CONTAINER
  79. $this->app->make('fs'); $this->app->make('fs', […]); $this->app->bind( 'fs', function ($app, $args) {…} );

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

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

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

    }
  83. Bonus

  84. Solving circular dependencies BONUS - CIRCULAR DEPENDENCIES

  85. $mailer = new Mailer(QueueInterface $queue); $queue = new Queue(MailerInterface $mailer);

    BONUS - CIRCULAR DEPENDENCIES
  86. $mailer = new Mailer(); $queue = new Queue(MailerInterface $mailer); $mailer->setQueueResolver(function

    () { return $this->app->make('queue'); }); BONUS CIRCULAR DEPENDENCIES
  87. recap: Reflection Binding Calling code Bonus

  88. @hannesvdvreken LaravelLive India 2017 Thank you!

  89. @hannesvdvreken LaravelLive India 2017 Time for questions.

  90. • http:/ /mwl.be REFERENCES