Pro Yearly is on sale from $80 to $50! »

IoC container - Laravel Groningen

IoC container - Laravel Groningen

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

May 23, 2017
Tweet

Transcript

  1. IOC container @hannesvdvreken Laravel Groningen

  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. But first: Design patterns WHAT IS IOC?

  13. class Service { public function calculate() { // Get stuff

    from FTP. // Do calculations. // Return result. } } 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. // Return 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. 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?
  24. 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?
  25. class FtpStorage implements Storage { public function __construct(Ftp $ftp) {

    $this->ftp = $ftp; } public function get($key) { // Get it and return it. } } WHAT IS IOC?
  26. Dependency inversion: Interface + implementation Dependency injection: Injecting `Storage` into

    `Service` Inversion of Control: The container builds your objects and calls them WHAT IS IOC?
  27. FtpStorage Service depends on depends on Ftp WHAT IS IOC?

  28. FtpStorage implements <interface> Storage Service depends on depends on Ftp

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

    IS IOC? implements
  30. new Service(new FtpStorage(new Ftp($settings))); WHAT IS IOC?

  31. Don’t create these instances yourself 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. Inversion of Control IoC container to the rescue WHAT IS

    IOC?
  34. IoC container is here to help you with Composition WHAT

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

    INJECTION
  36. Automatic constructor injection

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

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

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

  40. Reflection CONSTRUCTOR INJECTION

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

  42. Recursively CONSTRUCTOR INJECTION

  43. Helping the container

  44. Service Providers REGISTRATION

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

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

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

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

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

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

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

  52. $this->app->alias( Mailer::class, 'mailer' ); 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. Bind a concretion for every interface REGISTRATION - BINDING

  57. REGISTRATION - BINDING $this->app->bind( LoggerInterface::class, Monolog::class );

  58. Contextual binding REGISTRATION - CONTEXTUAL BINDING

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

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

  61. $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
  62. Inflection REGISTRATION

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

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

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

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

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

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

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

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

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

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

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

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

    ); REGISTRATION - CONTAINER EVENTS
  75. None
  76. Also useful for: - Quickly injecting stuff - Injecting soft

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

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

    CONTAINER EVENTS
  79. Using the container

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

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

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

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

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

    }
  85. Bonus

  86. Solving circular dependencies BONUS - CIRCULAR DEPENDENCIES

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

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

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

  90. @hannesvdvreken Thank you! Laravel Groningen

  91. @hannesvdvreken Time for questions. Laravel Groningen

  92. • http:/ /mwl.be REFERENCES